【问题标题】:Running total over repeating group by items based on time in Oracle SQL在 Oracle SQL 中根据时间对重复的按项目分组运行总计
【发布时间】:2016-05-04 14:21:58
【问题描述】:

我的第一篇文章,请多多包涵。我想根据一个按日期划分的值求和,但只需要日期的总和,而不是按项目分组的总和。几天来一直在研究这个问题,试图避免使用光标,但可能不得不这样做。

这是我正在查看的数据示例。顺便说一句,这是在 Oracle 11g 中。

 Key     Time               Amt
------ ------------------ ------
 Null    1-1-2016  00:00    50
 Null    1-1-2016  02:00    50
 Key1    1-1-2016  04:00    30
 Null    1-1-2016  06:00    30
 Null    1-1-2016  08:00    30
 Key2    1-1-2016  10:00    40
 Null    1-1-2016  12:00    40
 Key1    1-1-2016  14:00    30
 Null    1-2-2016  00:00    30
 Key2    1-2-2016  02:00    35

最终的结果应该是这样的:

 Key    Start            Stop             Amt
------ ---------------- ---------------- -----
 Null   1-1-2016 00:00   1-1-2016 02:00   100
 Key1   1-1-2016 04:00   1-1-2016 08:00    90
 Key2   1-1-2016 10:00   1-1-2016 12:00    80
 Key1   1-1-2016 14:00   1-2-2016 00:00    60
 key2   1-2-2016 02:00   1-2-2016 02:00    35

我已经获得了填写 Null 的密钥。密钥并不总是被输入,而是被假定为实际更改之前的值。

SELECT key ,time ,amt
FROM (
    SELECT DISTINCT amt, time, 
        ,last_value(amt ignore nulls) OVER (
            ORDER BY time
            ) key
    FROM sample
    ORDER BY time, amt
    )
WHERE amt > 0
ORDER BY time, key NULLS first;

但是当我试图获得一个运行总数时,即使有休息时间,它也会在键上求和。我无法弄清楚如何让它在钥匙上断裂。这是我最好的尝试,它不是很好,也不能正常工作。

SELECT key,time, amt 
     , sum(amt) OVER (PARTITION BY key ORDER BY time) AS running_total
  FROM (SELECT key, time, amt
          FROM (SELECT DISTINCT
                         amt,
                         time, 
                         last_value(amt ignore nulls) OVER (ORDER BY time) key
                  FROM sample
                 ORDER BY time, amt
               )
         WHERE amt > 0
         ORDER BY time, key NULLS first
       )
ORDER BY time, key NULLS first;

任何帮助将不胜感激。也许使用光标是唯一的方法。

匹配样本数据。

【问题讨论】:

  • 为什么Key 1的停止时间是8:00?

标签: sql oracle sum window-functions


【解决方案1】:

为了获得您要查找的总和,您需要一种方法来对您感兴趣的值进行分组。您可以使用几个 ROW_NUMBER 分析函数生成一个分组 ID,其中一个由键值分区.但是,由于您需要复制 KEY 列值,这需要分几个阶段完成:

WITH t1 AS (
  SELECT dta.*
       , last_value(KEY IGNORE NULLS)          -- Fill in the missing
               OVER (ORDER BY TIME ASC) key2   -- key values
    FROM your_data dta
), t2 AS (
  SELECT t1.*
       , row_number() OVER (ORDER BY TIME)     -- Generate a
       - row_number() OVER (PARTITION BY key2  -- grouping ID
                                ORDER BY TIME) gp
    FROM t1
)
SELECT t2.*
     , sum(amt) OVER (PARTITION BY gp, key2
                          ORDER BY TIME) running_sums
  FROM t2;

上述查询创建了一个 AMT 的运行总和,每次键值更改时都会重新启动。而用于代替上面最后一个 select 语句的以下查询给出了请求的结果,我不会将其称为运行总和。

SELECT key2
     , MIN(TIME) start_time
     , MAX(TIME) stop_time
     , sum(amt) amt
  FROM t2
 GROUP BY key2, gp;

要查看完整时间值,您可能需要更改会话 NLS_DATE_FORMAT,如下所示:

ALTER SESSION SET NLS_DATE_FORMAT='DD-MM-RRRR HH24:MI:SS';

或将每个日期列包装在 TO_CHAR 函数中以用于输出目的。

【讨论】:

    【解决方案2】:

    我不确定您的示例数据与查询有什么关系(例如,您的示例数据是一张表,而示例查询有很多查询)。但是,对于分配键,您可以使用 LAG()IGNORE NULLS 选项:

    select s.*,
           lag(key ignore nulls) over (order by start) as new_key
    from sample s;
    

    然后,您希望将相同键的组组合在一起。一种方法是行号不同。最后一步是聚合:

    select new_key, min(time), max(time), sum(amount)
    from (select s.*,
                 (row_number() over (order by start) -
                  row_number() over (partition by new_key order by start)
                 ) as grp
          from (select s.*,
                       lag(key ignore nulls) over (order by start) as new_key
                from sample s
               ) s
         ) s
    group by new_key, grp;
    

    【讨论】:

    • @Sentinel 。 . .我误解了问题的程度。我已经修改了答案以实际回答问题。
    • 仔细查看 lag 函数的结果,它并没有完全得到 OP 想要的,因为当键值确实发生变化时,它仍然显示键的先前值。除非您使用 coalescekeynew_key 值,否则这会使组偏离一行。事实证明,使用last_value 函数确实是更简单的方法。
    • 谢谢 - 成功了!还是保留了last_value函数。
    【解决方案3】:

    Key 不为NULL 时分配组号可以很容易地与LAG 一起计算:

    LAG(key ignore NULLS) OVER (ORDER BY time) AS new_key,
    COUNT(key) OVER (ORDER BY time ROWS UNBOUNDED PRECEDING) AS grp
    

    【讨论】:

      猜你喜欢
      • 2014-04-05
      • 2011-08-29
      • 2021-10-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多