SQL实现:将附加产品交易匹配至主产品最早符合时间窗的记录
问题背景与需求
现有两张结构一致的表t1和t2,字段包含client_id、transaction_date、price、product:
t1存储主产品交易数据t2存储附加产品交易数据
需求:判断同一客户的附加产品是否属于追加销售(附加产品交易发生在对应主产品交易的10天前后),并将附加产品价格匹配至主产品交易记录,需满足两个核心规则:
- 附加产品仅分配给时间窗内最早的主交易记录,避免重复统计;例如客户445在2024-04-05购买的附加产品,仅匹配至2024-04-01的主交易记录(2024-03-01的记录超出10天范围)。
- 同一客户多次购买附加产品时,需将附加产品总价汇总至最早符合条件的主交易记录,避免主交易记录重复;例如客户339的两次附加产品交易总价需仅匹配至2024-04-05的主交易记录。
用户尝试的SQL代码(仅解决第一个问题,无法处理第二个问题):
with tmp as (select t1.*, t2.price as xsell_price, t2.transaction_date as transaction_date_xsell from t1 left join t2 on t1.client_id = t2.client_id and t2.transaction_date between (t1.transaction_date - 5) and (t1.transaction_date + 5)), tmp2 as (select t1.*, row_number() over (partition by client_id, transaction_date_xsell order by transaction_date_xsell desc) as row_num from tmp) select t1.*, case when row_num = 1 then xsell_price end as xsell_price_calc from tmp2;
示例数据
t1表(主产品交易)
| client_id | transaction_date | price | product |
|---|---|---|---|
| 445 | 01.03.2024 | 100 | main |
| 445 | 01.04.2024 | 100 | main |
| 445 | 02.04.2024 | 100 | main |
| 339 | 05.04.2024 | 100 | main |
| 339 | 06.04.2024 | 100 | main |
t2表(附加产品交易)
| client_id | transaction_date | price | product |
|---|---|---|---|
| 445 | 05.04.2024 | 40 | additional |
| 339 | 05.04.2024 | 40 | additional |
| 339 | 06.04.2024 | 40 | additional |
期望输出
| client_id | transaction_date | price | product | xsell_price |
|---|---|---|---|---|
| 445 | 01.03.2024 | 100 | main | null |
| 445 | 01.04.2024 | 100 | main | 40 |
| 445 | 02.04.2024 | 100 | main | null |
| 339 | 05.04.2024 | 100 | main | 80 |
| 339 | 06.04.2024 | 100 | main | null |
解决方案
修正后的SQL代码
-- 第一步:为每个附加产品找到符合时间窗的最早主交易记录 WITH t2_matched AS ( SELECT t2.client_id, t2.transaction_date AS xsell_date, t2.price AS xsell_price, -- 找到该附加产品对应的最早主交易记录 FIRST_VALUE(t1.transaction_date) OVER ( PARTITION BY t2.client_id, t2.transaction_date ORDER BY t1.transaction_date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS matched_main_date FROM t2 LEFT JOIN t1 ON t1.client_id = t2.client_id AND t2.transaction_date BETWEEN t1.transaction_date - INTERVAL '10 days' AND t1.transaction_date + INTERVAL '10 days' WHERE t1.transaction_date IS NOT NULL -- 过滤掉无匹配主交易的附加产品 ), -- 第二步:按客户+匹配的主交易日期汇总附加产品总价 xsell_summary AS ( SELECT client_id, matched_main_date, SUM(xsell_price) AS total_xsell_price FROM t2_matched GROUP BY client_id, matched_main_date ) -- 第三步:将汇总后的附加价格关联回主交易表 SELECT t1.*, xsell_summary.total_xsell_price AS xsell_price FROM t1 LEFT JOIN xsell_summary ON t1.client_id = xsell_summary.client_id AND t1.transaction_date = xsell_summary.matched_main_date ORDER BY t1.client_id, t1.transaction_date;
逻辑说明
t2_matchedCTE:为每一条附加产品交易,关联所有符合10天时间窗的主交易记录,然后通过FIRST_VALUE窗口函数筛选出该附加产品对应的最早主交易日期,确保每个附加产品只匹配到最早的符合条件的主交易。xsell_summaryCTE:按客户和匹配的主交易日期分组,汇总该主交易对应的所有附加产品总价,解决同一客户多次附加产品的汇总问题。- 最后将汇总结果关联回
t1表,得到最终的匹配结果,未匹配到附加产品的主交易记录xsell_price为null。
注:代码中使用
INTERVAL '10 days'是标准SQL写法,若使用MySQL等数据库,可替换为DATE_ADD(t1.transaction_date, INTERVAL -10 DAY)和DATE_ADD(t1.transaction_date, INTERVAL 10 DAY)。
内容的提问来源于stack exchange,提问作者Artur
相关产品推荐
相关产品推荐

