我希望根据用户购买商品的时间将他们分为不同的组。
用户上次购买商品已超过 90 天,新的群组就会开始。
我正在寻找的示例:
行 | 用户ID | 日期 | 组中最小购买日期 | 组 |
---|---|---|---|---|
1 | 123 | 2023-04-01 | 2023-04-01 | 1 |
2 | 123 | 2023-04-01 | 2023-04-01 | 1 |
3 | 123 | 2023-04-23 | 2023-04-01 | 1 |
4 | 123 | 2023-05-07 | 2023-04-01 | 1 |
5 | 123 | 2023-06-04 | 2023-04-01 | 1 |
6 | 123 | 2023-06-29 | 2023-04-01 | 1 |
7 | 123 | 2023-07-09 | 2023-07-09 | 2 |
8 | 123 | 2023-07-16 | 2023-07-09 | 2 |
9 | 123 | 2023-07-16 | 2023-07-09 | 2 |
10 | 123 | 2023-08-25 | 2023-07-09 | 2 |
11 | 123 | 2023-09-04 | 2023-07-09 | 2 |
12 | 123 | 2023-10-11 | 2023-10-11 | 3 |
13 | 123 | 2023-10-16 | 2023-10-11 | 3 |
14 | 123 | 2023-12-16 | 2023-10-11 | 3 |
第 1 组均共享相同的 min_purchase_date_in_group,因为所有购买日期均在首次购买后 90 天内。
第 7 行是新组的开始,因为距首次购买日期 (2023-04-01) 已超过 90 天,并且第 2 组内的所有日期均距首次购买日期 (2023-07-09) 90 天)为团体。
第 12 行开始一个新组,因为该组的首次购买日期 (2023-10-11) 距第 2 组的首次访问日期 (2023-07-09) 已超过 90 天。
我能够在 Python 中执行此操作,但我希望使用 SQL 来解决此问题。
如果日期相距较远,我可以通过检查前一行值并确定是否已经超过 90 天来解决此问题。
如果日期更接近,就像我的示例一样,我的查询会将同一组分配给所有 14 行。
这是我目前解决 min_purchase_date_in_group 问题的方法:
select
user_id,
date,
case
when prev_date is null then date
when prev_date is not null and datediff(day, first_value(date) over(partition by user_id order by date rows between unbounded preceding and unbounded following), date) < 90 then first_value(date) over(partition by user_id order by date rows between unbounded preceding and unbounded following)
end as min_purchase_date_in_group
from purchases
它应用日期 2023-04-01 直到第 7 行,然后为空。我不确定如何使第 7 行变为 2023-07-09。
我也不确定如何确定组号。
有一种用于“间隙和孤岛”的 SQL 技术 [在数字或日期序列中查找一系列缺失值(间隙)或一系列连续值(孤岛)],这与您的需求相关。
首先,使用 LAG 函数对数据进行排序,以获取每行的前一个日期,同时按 user_id 进行分区
然后根据日期之间的 90 天差距或之前日期的缺失来计算更改指标 (isGroupChange)。
使用 SUM 窗口函数创建更改指示符的累积和,这会生成每组连续行的分组编号,直到下一个 isGroupChange。
--
WITH RankedPurchases
AS (
SELECT *
, LAG(min_purchase_date_in_group)
OVER (PARTITION BY user_id ORDER BY DATE) AS prev_min_purchase_date
FROM purchases
)
, GroupChanges
AS (
SELECT *
, CASE
WHEN prev_min_purchase_date IS NULL OR DATE - prev_min_purchase_date > 90 THEN 1
ELSE 0
END AS isGroupChange
FROM RankedPurchases
)
, GroupNumbering
AS (
SELECT *
, SUM(isGroupChange) OVER (
PARTITION BY user_id ORDER BY DATE
) AS purchase_group
FROM GroupChanges
)
SELECT
row
, user_id
, DATE
, min_purchase_date_in_group
, purchase_group
FROM GroupNumbering
ORDER BY DATE;
行 | 用户ID | 日期 | 组中最小购买日期 | 购买_组 |
---|---|---|---|---|
1 | 123 | 2023-04-01 | 2023-04-01 | 1 |
2 | 123 | 2023-04-01 | 2023-04-01 | 1 |
3 | 123 | 2023-04-23 | 2023-04-01 | 1 |
4 | 123 | 2023-05-07 | 2023-04-01 | 1 |
5 | 123 | 2023-06-04 | 2023-04-01 | 1 |
6 | 123 | 2023-06-29 | 2023-04-01 | 1 |
7 | 123 | 2023-07-09 | 2023-07-09 | 2 |
8 | 123 | 2023-07-16 | 2023-07-09 | 2 |
9 | 123 | 2023-07-16 | 2023-07-09 | 2 |
10 | 123 | 2023-08-25 | 2023-07-09 | 2 |
11 | 123 | 2023-09-04 | 2023-07-09 | 2 |
12 | 123 | 2023-10-11 | 2023-10-11 | 3 |
13 | 123 | 2023-10-16 | 2023-10-11 | 3 |
14 | 123 | 2023-12-16 | 2023-10-11 | 3 |