【问题标题】:Oracle sum group by date rangeOracle sum 按日期范围分组
【发布时间】:2016-04-22 09:38:06
【问题描述】:

我有下表

+-----------+-------+-------+
| Date      | Type  | Value |
+-----------+-------+-------+
| 1/1/2013  | A     | 1     |
| 1/2/2013  | A     | 3     |
| 1/3/2013  | A     | 5     |
| 1/4/2013  | A     | 6     |
| 1/6/2013  | A     | 8     |
| 1/7/2013  | A     | 1     |
| 1/8/2013  | A     | 2     |
+-----------+-------+-------+

我想对某一天前 3 个日期的值求和,所以我使用了这个查询。 即:sel_date = 2013 年 1 月 3 日。

select type, sum(value)
from table_name
where date <= seldate
and date > seldate - 3
group by type

现在的问题是,我想输出一个给定日期范围的表格,计算每个日期的前 3 天。

即:sel_date 范围 1/3/2013 - 1/8/2013

+-----------+-------+------------+
| Date      | Type  | Sum(Value) |
+-----------+-------+------------+
| 1/3/2013  | A     | 9          | // 5 + 3 + 1
| 1/4/2013  | A     | 14         | // 6 + 5 + 3
| 1/5/2013  | A     | 11         | // 0 + 6 + 5
| 1/6/2013  | A     | 14         | // 8 + 0 + 6
| 1/7/2013  | A     | 9          | // 1 + 8 + 0
| 1/8/2013  | A     | 11         | // 2 + 1 + 8
+-----------+-------+------------+

有没有办法在单个查询中做到这一点。我尝试阅读有关分区的内容,但它让我无处可去。

【问题讨论】:

  • 添加了跳过日期的测试用例

标签: oracle oracle11g


【解决方案1】:

在窗口子句中使用range between

select dt, type, value, 
       sum(value) over (order by dt range between 2 preceding and current row) as sv
  from t

测试数据和输出:

create table t (dt date, type varchar2(1), value number(5));
insert into t values (date '2013-01-01', 'A', 1);
insert into t values (date '2013-01-02', 'A', 3);
insert into t values (date '2013-01-03', 'A', 5);
insert into t values (date '2013-01-04', 'A', 6);
insert into t values (date '2013-01-05', 'A', 8);
insert into t values (date '2013-01-06', 'A', 1);
insert into t values (date '2013-01-07', 'A', 2);
insert into t values (date '2013-01-12', 'A', 2);

DT          TYPE  VALUE         SV
----------- ---- ------ ----------
2013-01-01  A         1          1
2013-01-02  A         3          4
2013-01-03  A         5          9
2013-01-04  A         6         14
2013-01-05  A         8         19
2013-01-06  A         1         15
2013-01-07  A         2         11
2013-01-12  A         2          2

【讨论】:

  • 你能解释一下这个范围是如何工作的吗?我的初始查询中没有指出,但如果它在数据中跳过一天会发生什么?
  • RANGE BETWEEN 2 PRECEDING AND CURRENT ROW 将只包括在ORDER BY 子句- 2 的值和当前行的值之间的数据(如果有多个具有相同值的行,那么它们将全部加在一起)。您也可以使用RANGE BETWEEN INTERVAL '2' DAY PRECEDING ... 明确声明这是您需要的一天间隔。如果您只想要当前行和前两行(无论是否跳过日期),请使用ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
  • @MT0 - 感谢您的帮助。 DRTauli - 以下是描述窗口子句如何工作的示例文章:link 1 查看 14.3.1 使用范围link 2 查看 windowing_clause
【解决方案2】:

你可以试试这样的:

with test(Date_, Type, Value ) as
(
  select to_date('01/01/2013', 'mm/dd/yyyy'), 'A', 1 from dual union all
  select to_date('01/02/2013', 'mm/dd/yyyy'), 'A', 3 from dual union all
  select to_date('01/03/2013', 'mm/dd/yyyy'), 'A', 5 from dual union all
  select to_date('01/04/2013', 'mm/dd/yyyy'), 'A', 6 from dual union all
  select to_date('01/05/2013', 'mm/dd/yyyy'), 'A', 8 from dual union all
  select to_date('01/06/2013', 'mm/dd/yyyy'), 'A', 1 from dual union all
  select to_date('01/07/2013', 'mm/dd/yyyy'), 'A', 2 from dual
)
select *
from (
        select date_, type,
               value + nvl(lag(value, 1) over (partition by type order by date_), 0)
                     + nvl(lag(value, 2) over (partition by type order by date_), 0) as value
        from test
)        
where date_ between to_date('01/03/2013', 'mm/dd/yyyy') and to_date('01/07/2013', 'mm/dd/yyyy')

这会根据日期对每一行的前两行的值求和;外部查询仅用于应用过滤器,因为在内部查询中应用它会导致错误的总和。

LAG 用于从当前行之前 1 或 2 个位置的行中读取值。

【讨论】:

  • 这个范围没问题,但为了安全起见,我认为您需要 nvl() 滞后值;如果您查询表中的前两个日期,则结果为空。 (我猜这种情况下的行为无论如何都不是由 OP 定义的,但仍然......也许在外部查询中添加 and value is not null 可能是他们想要的)
  • 这可行,但可以说我想要一个 90 天的总和,然后我会延迟 89 次。
  • 如果您需要广泛/可变的天数,我相信最好的解决方案来自 Ponder Stibbons
  • 这相当于使用SUM( value ) OVER ( ORDER BY Date_ ROWS BETWEEN 2 PRECEDING AND CURRENT ROW ) 执行windowed analytic function,如果有跳过的天数,那么它将获得当前行前2 天以上的结果。
【解决方案3】:

你可以用这个:

select date1 ,type,
     (select sum(t1.value) sumvalue from table_name t1 where t1.date1 between (t2.date1 - 2) and t2.date1 )
from table_name t2
where date1 between startDate and endDate

【讨论】:

  • 可以,但分析方法会更有效,因为它们不必多次敲击表。
【解决方案4】:
select t.date, sum(t.value) OVER(ORDER BY t.date ROWS BETWEEN 2 PRECEDING AND 0 FOLLOWING) as Pre_3row_sum
from table_name t

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-04
    • 2021-11-16
    • 2013-05-19
    • 2010-10-31
    • 2016-09-22
    • 2019-02-19
    相关资源
    最近更新 更多