【发布时间】:2020-05-01 21:41:48
【问题描述】:
我正在事实表(例如发票历史记录)之上构建功能,该表将简单地继续附加到右侧。一个基本的发票历史表可能如下所示:
| date | customer | product | amount | feature c-p (past 5 days) | ...
-----------------------------------------------------------------------------------
| 2020/01/01 | CA | P1 | 10 | NA |
| 2020/01/02 | CA | P1 | 5 | 10 = 10 |
| 2020/01/05 | CA | P1 | 20 | 15 = 5 + 10 |
| 2020/01/07 | CA | P1 | 20 | 25 = 20 + 5 |
(01/01 out of range above) |
| 2020/01/15 | CA | P1 | 100 | 25 = 10 + 5 + 20 |
| 2020/01/17 | CA | P1 | 200 | 100 = 100 |
| 2020/01/31 | CA | P1 | 20 | 0 = 0 |
一开始,我们将使用自连接的逻辑写成类似于:
select
c.date,
c.customer,
c.product,
c.amount,
sum(c.amount2)
from
(select
i1.*,
i2.date as date2,
i2.amount as amount2
from invoice i1
inner join invoice i2
on i1.customer = i2.customer
and i1.product = i2.product
and i1.date < i2.date and i1.date >= i2.date - 5 -- where we customize the window
) c
group by
c.date,
c.customer,
c.product,
c.amount
如果我没记错的话,这个自连接本身是 O(N^2),但逻辑很简单,每个人都可以理解。但直到最近,当我们开始使用一张大桌子时,这种方法才爆发。
我之前在考虑窗口函数,但我不确定是否有更高效(计算效率和存储效率更高)的方法?
我的想法是使用窗口函数,但看起来我的逻辑是自定义的超出范围,而不是固定的 N 行回溯,而是应该回溯 5 天?在 Hive/Impala 中是否有可能,如果没有,我想我将不得不填写缺失的日期,然后使用 windows 功能。愿意接受任何建议吗?
(今天我们使用的是 Hive/Impala,但如果其他数据库中确实有更有效的方法,我当然愿意接受)。
更新
刚刚运行了一个使用 2000 万行真实数据的基准测试,节省了大量时间:
- 自加入过滤:128 分钟
- 使用包含日期转换的窗口函数:15 分钟(Gordon 的回答),最重要的是,这种方法保证不会引入重复,因为同一客户和同一产品可能在同一天被购买多次
- Hive 不支持内联相关子查询,但 GBM 的解决方案应该能够有效避免完全笛卡尔连接
【问题讨论】: