每天窗口函数的SQL性能问题

问题描述

给定约2300万用户,计算给定一天(即使未执行任何登录)在最近X个月内的累计登录数的最有效方法是什么?客户的开始日期是其首次登录,结束日期是今天。

所需的输出

c_id        day            nb_logins_past_6_months
----------------------------------------------
1           2019-01-01     10
1           2019-01-02     10
1           2019-01-03     9
...
1           today          5

➔每位用户每天一行,包含当前日期和过去179天之间的登录次数

方法1

 1. Cross join each customer ID with calendar table
 2. Left join on login table on day
 3. Compute window function (i.e. `sum(nb_logins) over (partition by c_id order by day rows between 179 preceding and current row)`)

+ Easy to understand and mantain
- Really heavy,quite impossible to run on daily basis
- Incremental does not bring much benefit : still have to go 179 days in the past

方法2

 1. Cross join each customer ID with calendar table 
 2. Left join on login table on day between today and 179 days in the past
 3. Group by customer ID and day to get nb logins within 179 days

+ Easier to do incremental
- Table at step 2 is exceeding 300 billion rows

知道这不是唯一的用例时,通常的处理方法是什么,我们必须计算其他类似的列(过去12个月中的nb次登录等)

解决方法

在标准SQL中,您将使用:

select l.*,count(*) over (partition by customerid
                      order by login_date
                      range between interval '6 month' preceding and current row
                     ) as num_logins_180day
from logins l;

这假设logins表的登录日期没有时间成分。

我认为没有理由将2300万用户乘以180天来生成超过400万行的结果集来回答这个问题。

,

为了提高性能,请不要一次完成全部任务。取而代之的是,在每个月末(或一天或对您的数据有意义的任何事情)收集小计。然后2^32向上分类汇总以提供“报告”。

更多讨论(重点是MySQL):http://mysql.rjweb.org/doc.php/summarytables

(您应该使用特定产品标记问题;不同的产品具有不同的语法/功能/性能等)

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...