【问题标题】:Oracle select sum by time windowOracle 按时间窗口选择总和
【发布时间】:2014-05-15 20:51:05
【问题描述】:

假设我们有以下格式和数据的 ORACLE 表:

TIMESTAMP                 MESSAGENO              ORGMESSAGE                           
------------------------- ---------------------- -------------------------------------
27.04.13                  1                      START PERIOD  
27.04.13                  3                      10
27.04.13                  4                      5
28.04.13                  5                      6
28.04.13                  3                      20
29.04.13                  4                      25
29.04.13                  5                      26
30.04.13                  2                      END PERIOD
30.04.13                  1                      START PERIOD  
01.05.13                  3                      10
02.05.13                  4                      15
02.05.13                  5                      16
03.05.13                  3                      30
03.05.13                  4                      35
04.05.13                  5                      36
05.05.13                  2                      END PERIOD

我想选择按 MESSAGENO 分组的所有期间(START PERIOD 和 END PERIOD 之间的窗口)的所有 ORGMESSAGE 的总和。

示例输出为:

PERIOD START  PERIOD END   MESSAGENO SUM 
------------ ------------- --------  ----
27.04.13     30.04.13        3        25
27.04.13     30.04.13        4        30
27.04.13     30.04.13        5        32
30.04.13     05.05.13        3        45
30.04.13     05.05.13        4        50
30.04.13     05.05.13        5        52

我猜想使用 ORACLE Analityc 函数是合适的,但真的不知道如何以及从哪里开始。 提前感谢您的帮助。

【问题讨论】:

  • 请澄清。显然,您不要按 messageno 分组,因为结果列表中有重复的 messageno。
  • 我想我知道您想做什么,但是您的输入数据中缺少某些内容。由于没有可靠的排序列,你怎么知道日期为 30.04.13 的哪一行是“第一”行?

标签: sql oracle select


【解决方案1】:

如果我们假设句点开始和结束匹配,那么找到匹配消息的一种简单方法是计算前面的开始次数。这是一个累积和,在 Oracle 中很容易。剩下的只是聚合:

select min(timestamp) as periodstart, max(timestamp) as periodend, messageno, count(*)
from (select om.*,
             sum(case when messageno = 1 then 1 else 0 end) over (order by timestamp) as grp
      from orgmessages om
     ) om
where messageno not in (1, 2)
group by grp, messageno;

请注意,此方法(与其他方法一样)确实希望时间戳在每条记录上都是唯一的。在提供的数据中,这些解决方案将起作用。但是,如果您在同一天有多个开始和结束,那么假设 timestamp 只有日期,它们都不会起作用。

【讨论】:

  • messageno不应该在group by中吗?
【解决方案2】:

首先查找每个期间开始的所有期间结束。然后加入你的表进行分组和求和。

select 
  dates.start_date, 
  dates.end_date, 
  messageno, 
  sum(to_number(orgmessage)) as period_sum
from mytable
join
(
  select start_dates.timestmp as start_date, min(end_dates.timestmp) as end_date
  from (select * from mytable where orgmessage = 'START PERIOD') start_dates
  join (select * from mytable where orgmessage = 'END PERIOD') end_dates
    on start_dates.timestmp < end_dates.timestmp
  group by start_dates.timestmp
) dates on mytable.timestmp between dates.start_date and dates.end_date
where mytable.orgmessage not like '%PERIOD%'
group by dates.start_date, dates.end_date, messageno
order by dates.start_date, dates.end_date, messageno;

SQL 小提琴:http://www.sqlfiddle.com/#!4/365de/15.

【讨论】:

    【解决方案3】:

    请试试这个,用你的表名替换rrr

    select periodstart, periodend, messageno, sum(to_number(orgmessage)) s
    from (select TIMESTAMP periodstart,
                 (select min (TIMESTAMP) from rrr r2 where orgmessage = 'END PERIOD' and r2.TIMESTAMP > r.TIMESTAMP) periodend
          from rrr r
          where orgmessage = 'START PERIOD'
    ) borders, rrr r
    where r.TIMESTAMP between borders.periodstart and borders.periodend 
          and r.orgmessage not in ('END PERIOD', 'START PERIOD')
    group by periodstart, periodend, messageno
    order by periodstart, periodend, messageno
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-12
      • 2019-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-24
      • 1970-01-01
      相关资源
      最近更新 更多