Oracle SQL 从行总和计算一个不同行的差异
Oracle SQL Calculate Difference to one distinct row from sum of rows
我有以下 table:
CREATE TABLE tablename ("ID" varchar2(1), "Type" varchar2(3), "Value" int);
INSERT ALL
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MS',2)
INTO tablename ("ID","Type", "Value")
VALUES ('A', 'MS', 5)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSH', 6)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSH', 10)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSO', -5)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSO', 12)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MS',5)
INTO tablename ("ID","Type", "Value")
VALUES ('B', 'MS', -4)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSH', 2)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSH', 11)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSO', -5)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSO', 13)
SELECT * FROM dual
;
table 将按 ID 和类型分组,并使用值的总和。
现在我想得到每个 ID 的 MS-MSH 和 MS-MSO 的区别。
所以结果应该是这样的
ID | Type | sum(value) | Dif
A | MS | 7 | 0
A | MSH | 16 | -9
A | MSO | 7 | 0
B | MS | 1 | 0
B | MH | 13 | -12
B | MSO | 9 | -8
这是 table 可以使用的
(更多评论)
我没有测试过,我会沿着这些方向尝试一些东西。希望这个对你有帮助。抱歉,我还没有测试过。
with cte as
(
select t.id,t.type,sum(t.value) as sumval
from
tablename as t
group by t.id,t.type
)
select c.*,
case c.type
when 'ms' then 0
else (c.sumval-(select c2.sumval from cte c2 where c2.id=c.id and c2.type='ms'))
end dif
from cte c
order by c.id,c.type
如果你有 "Types"
像 'MS1', 'MSH1', 'MSO1', 'MS2', 'MSO2'...
,查询有效,差异由 "ID"
和 "Type"
字符串末尾的数字分区,查询只看 1 table 没有 sub-querys 和 cte,没有 case 语句。
查询:
select "ID","Type",sum("Value") Sum_value,
FIRST_VALUE (sum("Value")) over (partition by "ID",
nvl(regexp_substr("Type",'[0-9]{1,}$'),'0')
ORDER BY "ID")-sum("Value")
diff
from tablename
group by ("ID","Type")
示例数据:
CREATE TABLE tablename ("ID" varchar2(1), "Type" varchar2(3), "Value" int);
INSERT ALL
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MS',2)
INTO tablename ("ID","Type", "Value")
VALUES ('A', 'MS', 5)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSH', 6)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSH', 10)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSO', -5)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSO', 12)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MS',5)
INTO tablename ("ID","Type", "Value")
VALUES ('B', 'MS', -4)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSH', 2)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSH', 11)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSO', -5)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSO', 13)
SELECT * FROM dual
;
结果:
ID Type SUM_VALUE DIFF
----------------------
A MS 7 0
A MSH 16 -9
A MSO 7 0
B MS 1 0
B MSH 13 -12
B MSO 8 -7
用LAG
解析函数:
SQL> with temp as
2 (select id, type, sum(value) sumval
3 from tablename
4 group by id, type
5 )
6 select id, type, sumval,
7 --
8 -case when type = 'MS' then 0
9 when type = 'MSH' then sumval - lag(sumval, 1) over (partition by id order by type)
10 when type = 'MSO' then sumval - lag(sumval, 2) over (partition by id order by type)
11 end diff
12 from temp
13 order by id, type;
ID TYPE SUMVAL DIFF
-- ---- ---------- ----------
A MS 7 0
A MSH 16 -9
A MSO 7 0
B MS 1 0
B MSH 13 -12
B MSO 8 -7
6 rows selected.
SQL>
与您的问题无关,但是 - 这是 Oracle。避免双引号和大小写混合标识符,你只会遇到这些问题。
您可以将聚合函数包装在一个分析函数中,并使用条件聚合找到具有相同 id
的 type
的 MS
值(然后您只读取 table 一次并且不需要任何相关的 sub-queries 或 CTE):
SELECT id,
type,
sum(value) as sumval,
MAX(CASE type WHEN 'MS' THEN SUM(value) END) OVER (PARTITION BY id)
- SUM(value) AS diff
FROM tablename
GROUP BY
id,
type;
其中,对于示例数据:
CREATE TABLE tablename (ID, Type, Value) AS
SELECT 'A', 'MS', 2 FROM DUAL UNION ALL
SELECT 'A', 'MS', 5 FROM DUAL UNION ALL
SELECT 'A', 'MSH', 6 FROM DUAL UNION ALL
SELECT 'A', 'MSH', 10 FROM DUAL UNION ALL
SELECT 'A', 'MSO', -5 FROM DUAL UNION ALL
SELECT 'A', 'MSO', 12 FROM DUAL UNION ALL
SELECT 'B', 'MS', 5 FROM DUAL UNION ALL
SELECT 'B', 'MS', -4 FROM DUAL UNION ALL
SELECT 'B', 'MSH', 2 FROM DUAL UNION ALL
SELECT 'B', 'MSH', 11 FROM DUAL UNION ALL
SELECT 'B', 'MSO', -5 FROM DUAL UNION ALL
SELECT 'B', 'MSO', 13 FROM DUAL;
输出:
ID
TYPE
SUMVAL
DIFF
A
MS
7
0
A
MSH
16
-9
A
MSO
7
0
B
MS
1
0
B
MSH
13
-12
B
MSO
8
-7
db<>fiddle here
我有以下 table:
CREATE TABLE tablename ("ID" varchar2(1), "Type" varchar2(3), "Value" int);
INSERT ALL
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MS',2)
INTO tablename ("ID","Type", "Value")
VALUES ('A', 'MS', 5)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSH', 6)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSH', 10)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSO', -5)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSO', 12)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MS',5)
INTO tablename ("ID","Type", "Value")
VALUES ('B', 'MS', -4)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSH', 2)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSH', 11)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSO', -5)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSO', 13)
SELECT * FROM dual
;
table 将按 ID 和类型分组,并使用值的总和。 现在我想得到每个 ID 的 MS-MSH 和 MS-MSO 的区别。
所以结果应该是这样的
ID | Type | sum(value) | Dif
A | MS | 7 | 0
A | MSH | 16 | -9
A | MSO | 7 | 0
B | MS | 1 | 0
B | MH | 13 | -12
B | MSO | 9 | -8
这是 table 可以使用的
(更多评论) 我没有测试过,我会沿着这些方向尝试一些东西。希望这个对你有帮助。抱歉,我还没有测试过。
with cte as
(
select t.id,t.type,sum(t.value) as sumval
from
tablename as t
group by t.id,t.type
)
select c.*,
case c.type
when 'ms' then 0
else (c.sumval-(select c2.sumval from cte c2 where c2.id=c.id and c2.type='ms'))
end dif
from cte c
order by c.id,c.type
如果你有 "Types"
像 'MS1', 'MSH1', 'MSO1', 'MS2', 'MSO2'...
,查询有效,差异由 "ID"
和 "Type"
字符串末尾的数字分区,查询只看 1 table 没有 sub-querys 和 cte,没有 case 语句。
查询:
select "ID","Type",sum("Value") Sum_value,
FIRST_VALUE (sum("Value")) over (partition by "ID",
nvl(regexp_substr("Type",'[0-9]{1,}$'),'0')
ORDER BY "ID")-sum("Value")
diff
from tablename
group by ("ID","Type")
示例数据:
CREATE TABLE tablename ("ID" varchar2(1), "Type" varchar2(3), "Value" int);
INSERT ALL
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MS',2)
INTO tablename ("ID","Type", "Value")
VALUES ('A', 'MS', 5)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSH', 6)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSH', 10)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSO', -5)
INTO tablename ("ID", "Type", "Value")
VALUES ('A', 'MSO', 12)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MS',5)
INTO tablename ("ID","Type", "Value")
VALUES ('B', 'MS', -4)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSH', 2)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSH', 11)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSO', -5)
INTO tablename ("ID", "Type", "Value")
VALUES ('B', 'MSO', 13)
SELECT * FROM dual
;
结果:
ID Type SUM_VALUE DIFF
----------------------
A MS 7 0
A MSH 16 -9
A MSO 7 0
B MS 1 0
B MSH 13 -12
B MSO 8 -7
用LAG
解析函数:
SQL> with temp as
2 (select id, type, sum(value) sumval
3 from tablename
4 group by id, type
5 )
6 select id, type, sumval,
7 --
8 -case when type = 'MS' then 0
9 when type = 'MSH' then sumval - lag(sumval, 1) over (partition by id order by type)
10 when type = 'MSO' then sumval - lag(sumval, 2) over (partition by id order by type)
11 end diff
12 from temp
13 order by id, type;
ID TYPE SUMVAL DIFF
-- ---- ---------- ----------
A MS 7 0
A MSH 16 -9
A MSO 7 0
B MS 1 0
B MSH 13 -12
B MSO 8 -7
6 rows selected.
SQL>
与您的问题无关,但是 - 这是 Oracle。避免双引号和大小写混合标识符,你只会遇到这些问题。
您可以将聚合函数包装在一个分析函数中,并使用条件聚合找到具有相同 id
的 type
的 MS
值(然后您只读取 table 一次并且不需要任何相关的 sub-queries 或 CTE):
SELECT id,
type,
sum(value) as sumval,
MAX(CASE type WHEN 'MS' THEN SUM(value) END) OVER (PARTITION BY id)
- SUM(value) AS diff
FROM tablename
GROUP BY
id,
type;
其中,对于示例数据:
CREATE TABLE tablename (ID, Type, Value) AS
SELECT 'A', 'MS', 2 FROM DUAL UNION ALL
SELECT 'A', 'MS', 5 FROM DUAL UNION ALL
SELECT 'A', 'MSH', 6 FROM DUAL UNION ALL
SELECT 'A', 'MSH', 10 FROM DUAL UNION ALL
SELECT 'A', 'MSO', -5 FROM DUAL UNION ALL
SELECT 'A', 'MSO', 12 FROM DUAL UNION ALL
SELECT 'B', 'MS', 5 FROM DUAL UNION ALL
SELECT 'B', 'MS', -4 FROM DUAL UNION ALL
SELECT 'B', 'MSH', 2 FROM DUAL UNION ALL
SELECT 'B', 'MSH', 11 FROM DUAL UNION ALL
SELECT 'B', 'MSO', -5 FROM DUAL UNION ALL
SELECT 'B', 'MSO', 13 FROM DUAL;
输出:
ID TYPE SUMVAL DIFF A MS 7 0 A MSH 16 -9 A MSO 7 0 B MS 1 0 B MSH 13 -12 B MSO 8 -7
db<>fiddle here