Oracle 按时间范围按 ID 聚合

Posted

技术标签:

【中文标题】Oracle 按时间范围按 ID 聚合【英文标题】:Oracle agregate by ID with time range 【发布时间】:2021-02-18 10:39:27 【问题描述】:

我确定我在某处看到它,但我找不到它。 鉴于此表历史性:

ID1 ID2 Event_Date Label
1 1 2020-01-01 1
1 1 2020-01-02 1
1 1 2020-01-04 1
1 1 2020-01-08 1
1 1 2020-01-20 1
1 1 2020-12-30 1
1 1 2020-01-01 0
1 1 2020-01-02 1
1 1 2020-01-04 0
1 1 2020-01-08 1
1 1 2020-01-20 0
1 1 2020-12-30 1
1 2 2020-01-01 1
1 2 2020-01-02 1
1 2 2020-01-04 1
2 1 2020-01-08 1
2 1 2020-01-20 1
2 1 2020-12-30 1

以及表格起点

ID1 ID2 Event_Date
1 1 2020-01-01
1 1 2020-01-02
1 1 2020-01-05
1 1 2020-01-08
1 1 2020-01-21
1 1 2021-01-01
1 1 2020-01-01
1 1 2020-01-03
1 1 2020-01-06
1 1 2020-01-11
1 1 2020-01-20
1 1 2020-12-31
1 2 2020-01-03
1 2 2020-01-05
1 2 2020-01-08
2 1 2020-01-08
2 1 2020-01-21
2 1 2021-01-01

对于起始点中的每一行,计算历史记录中具有相同 ID1 和 ID2 的行数,其中历史记录中的 Event_Date 介于 StartingPoint.Event_date - n 天之间(我将其设为 n 以便我可以更改不同的值)和StartingPoint.Event_date - 2 天。然后使用相同的规则计算 label = 1 的行的分数。

我知道我可以用 join 来做到这一点,但是如果历史和起点非常大,这看起来效率很低(对于起点中的每一行,它都会创建一个大的连接,最后它会汇总相同的集合行多次重复)。从抽象的角度来看,在我看来,最好先汇总每个 ID1、ID2、Event_date 的历史记录,并与起点连接并选择最佳的,但我对其他解决方案持开放态度。

【问题讨论】:

你必须加入他们,所以不太清楚你的意思。如果您有一些可行但您认为效率低下的东西(以及您为什么这么认为),则包含您对此数据的期望结果和当前查询可能会有所帮助。 【参考方案1】:

您可以使用子查询尝试以下解决方案:

select * ,(select count(*) from historic h where h.id1=s.id1 and h.id2=s.id2 and h.event_date between dateadd(day,-30,s.event_date) and dateadd(day,-2,s.event_date) )from startingpoint  s

【讨论】:

【参考方案2】:

你必须有某种形式的加入;要么直接加入,要么使用标量子查询,这可能不会那么有效。

如果您只想查看具有历史数据的行,最简单的方法可能只是简单的连接:

select sp.id1, sp.id2, sp.event_date,
  count(h.event_date) as any_label,
  count(case when h.label = 1 then h.label end) as label_1,
  count(case when h.label = 1 then h.label end) / count(h.event_date) as fraction_1
from startingpoint sp
join historic h on h.id1 = sp.id1
and h.id2 = sp.id2
and h.event_date >= sp.event_date - 10
and h.event_date < sp.event_date - 2
group by sp.id1, sp.id2, sp.event_date
order by sp.id1, sp.id2, sp.event_date;

其中 n 为 10;你的数据会给你:

ID1 ID2 EVENT_DATE ANY_LABEL LABEL_1           FRACTION_1
--- --- ---------- --------- ------- --------------------
  1   1 2020-01-05         4       3                  .75
  1   1 2020-01-06         4       3                  .75
  1   1 2020-01-08         6       4 .6666666666666666667
  1   1 2020-01-11         8       6                  .75
  1   2 2020-01-05         2       2                    1
  1   2 2020-01-08         3       3                    1

或者如果你想看到零计数,你可以使用外连接;但是分数计算需要一些逻辑来避免被零除的错误:

select sp.id1, sp.id2, sp.event_date,
  count(h.event_date) as any_label,
  count(case when h.label = 1 then h.label end) as label_1,
  case when count(h.event_date) > 0 then
    count(case when h.label = 1 then h.label end) / count(h.event_date)
  end as fraction_1
from startingpoint sp
left join historic h on h.id1 = sp.id1
and h.id2 = sp.id2
and h.event_date >= sp.event_date - 10
and h.event_date < sp.event_date - 2
group by sp.id1, sp.id2, sp.event_date
order by sp.id1, sp.id2, sp.event_date;

得到:

ID1 ID2 EVENT_DATE ANY_LABEL LABEL_1           FRACTION_1
--- --- ---------- --------- ------- --------------------
  1   1 2020-01-01         0       0
  1   1 2020-01-02         0       0
  1   1 2020-01-03         0       0
  1   1 2020-01-05         4       3                  .75
  1   1 2020-01-06         4       3                  .75
  1   1 2020-01-08         6       4 .6666666666666666667
  1   1 2020-01-11         8       6                  .75
  1   1 2020-01-20         0       0
  1   1 2020-01-21         0       0
  1   1 2020-12-31         0       0
  1   1 2021-01-01         0       0
  1   2 2020-01-03         0       0
  1   2 2020-01-05         2       2                    1
  1   2 2020-01-08         3       3                    1
  2   1 2020-01-08         0       0
  2   1 2020-01-21         0       0
  2   1 2021-01-01         0       0

db<>fiddle

【讨论】:

以上是关于Oracle 按时间范围按 ID 聚合的主要内容,如果未能解决你的问题,请参考以下文章

按聚合对范围内的缺失值进行分组

在日期范围内按日期聚合数据,结果集中没有日期间隔

elasticsearch按范围聚合

Oracle - 按类别分组,日期范围[重复]

SQL:如何按特定范围的 ID 编号,然后按时间排序?

如何设计 DynamoDB 表以方便按时间范围搜索和按唯一 ID 删除