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。避免双引号和大小写混合标识符,你只会遇到这些问题。

您可以将聚合函数包装在一个分析函数中,并使用条件聚合找到具有相同 idtypeMS 值(然后您只读取 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