Oracle SQL 在字段更改时运行总计(仅在字段更改时对列求和)

Posted

技术标签:

【中文标题】Oracle SQL 在字段更改时运行总计(仅在字段更改时对列求和)【英文标题】:Oracle SQL running total on change of field (SUM on column only when field changes) 【发布时间】:2018-01-17 20:24:35 【问题描述】:

我有一个关于如何仅在字段更改时对列求和的问题。

以下表为例:

请注意,A 列和 B 列是不同的表。 IE。 A从表X中选择,B从表Y中选择

选择 X.A、Y.B 来自 X 在 X.DATE = Y.DATE 和 X.VAL1 = 上的内部连接 ​​Y Y.VAL1 和 X.VAL2 = Y.VAL2

 A     B
123    5
123    5
456    10
789    15
789    15

我需要对 A 列字段更改的 B 列求和:

即查询应该返回 5 + 10 + 15 = 30 (第一次是 5,因为 A 列中的值是 123,第二次是 10,因为 A 列从 123 变为 456 - 注意第二行是跳过,因为 A 列仍然包含值 123 - 因此字段逻辑的变化等等)。

我不能做一个简单的SUM(B),因为那样会返回 50。我也不能做SUM(B) OVER (PARTITION BY A),因为那会按组计算,而不是按字段变化计算。

我的输出需要如下所示:

A    B    X
123  5    5
123  5    5
456  10   15
789  15   30
789  15   30

我试图在一个简单的查询中做到这一点。我可以使用特定的功能来执行此操作吗?

【问题讨论】:

是否可能有两行 A 的值相同但 B 的值不同? @Aleksej 不,B 的值将始终相同。重复项来自 A。请注意,A 列是一个表,B 列是另一个表 【参考方案1】:

对于提供的简单数据集,以下应该有效。当然,您需要查看 ORDER BY 子句在您的确切用例中的正确性。

SELECT a
      ,b
      ,SUM(CASE WHEN a = prev_a THEN 0 ELSE b END) OVER (ORDER BY a RANGE UNBOUNDED PRECEDING) AS x
  FROM (
SELECT a
      ,b
      ,LAG(a) OVER (ORDER BY a) AS prev_a
  FROM your_query
  )

此解决方案使用LAG 函数,该函数从先前的结果返回指定的列。然后外部查询的SUM 仅在前一行没有相同值时才给出该值。 SUM 中还涉及到窗口子句,因为您指定了需要运行总计。

【讨论】:

【参考方案2】:

咩咩?

SQL> with test (a, b) as
  2  (select 123, 5 from dual union all
  3   select 123, 5 from dual union all
  4   select 456, 10 from dual union all
  5   select 789, 15 from dual union all
  6   select 789, 15 from dual
  7  ),
  8  proba as(
  9  select a, b,
 10    case when a <> nvl(lag(a) over (order by a), 0) then 'Y' else 'N' end switch
 11  from test
 12  )
 13  select a, b,
 14    sum(decode(switch, 'Y', b, 0)) over (partition by null order by a) x
 15  from proba
 16  order by a;

         A          B          X
---------- ---------- ----------
       123          5          5
       123          5          5
       456         10         15
       789         15         30
       789         15         30

SQL>

【讨论】:

【参考方案3】:

您也可以创建一个函数并使用它,请参见下面的示例,

create package test_pkg123
as
  a number;
  r_sum NUMBER;
  function get_r_sum(p_a number, p_val NUMBER, rown NUMBER) return number;
end;
/

create or replace package body test_pkg123
as
function get_r_sum(p_a number, p_val NUMBER, rown NUMBER) return number
  is
  begin
      if rown = 1 then
          r_sum := p_val;
          return r_sum;
      end if;
      if p_a != a then
         r_sum := nvl(r_sum, 0) + nvl(p_val, 0);
      end if;
      a := p_a;
      return r_sum;
  end;
end;
/

with test (a, b) as
    (select 123, 5 from dual union all
     select 123, 5 from dual union all
     select 456, 10 from dual union all
     select 789, 15 from dual union all
     select 789, 15 from dual union all
     select 789, 15 from dual union all
     select 123, 2 from dual
    )
select a, b, test_pkg123.get_r_sum(a, b, rownum) r_sum 
  from test;

输出:

     A          B      R_SUM
   123          5          5
   123          5          5
   456         10         15
   789         15         30
   789         15         30
   789         15         30
   123          2         32

已选择 7 行

【讨论】:

以上是关于Oracle SQL 在字段更改时运行总计(仅在字段更改时对列求和)的主要内容,如果未能解决你的问题,请参考以下文章

在新页面上重置“运行总计字段”

如何使用 pl sql 过程从结构仅在运行时知道的 oracle 表中动态获取数据?

Oracle SQL 分析查询 - 类似电子表格的递归运行总计

计算 SQL 中日期字段的计数和运行总计

SQL 表更改仅在第二个程序运行后可见

计算值在分区上更改时的运行总计