如何在SQL中基于日期映射匹配历史数据的对应Stage值?
问题:匹配时间点对应的历史Stage值,保留Table A全部数据
Table A
| ID | Point_date | TENANT |
|---|---|---|
| 114396845 | 1/5/21 | vp.com |
| 114396845 | 1/5/21 | vp.com |
| 114396845 | 1/8/21 | vp.com |
| 114396845 | 1/8/21 | vp.com |
| 114396845 | 1/8/21 | vp.com |
| 114396845 | 1/8/21 | vp.com |
| 114396845 | 3/30/21 | vp.com |
| 114396845 | 4/5/21 | vp.com |
| 756392741 | 5/27/21 | vp.com |
| 756392741 | 7/22/21 | vp.com |
| 756392741 | 7/22/21 | vp.com |
| 756392741 | 11/29/21 | vp.com |
| 756392741 | 11/29/21 | vp.com |
| 756392741 | 12/27/21 | vp.com |
| 756392741 | 1/24/22 | vp.com |
| 756392741 | 1/28/22 | vp.com |
| 756392741 | 1/29/22 | vp.com |
| 756392741 | 2/6/22 | vp.com |
| 756392741 | 2/8/22 | vp.com |
| 756392741 | 2/10/22 | vp.com |
| 756392741 | 2/10/22 | vp.com |
| 756392741 | 2/10/22 | vp.com |
| 756392741 | 2/14/22 | vp.com |
| 756392741 | 2/18/22 | vp.com |
| 756392741 | 2/18/22 | vp.com |
| 756392741 | 2/23/22 | vp.com |
| 756392741 | 2/23/22 | vp.com |
| 756392741 | 3/6/22 | vp.com |
| 756392741 | 3/10/22 | vp.com |
| 756392741 | 3/10/22 | vp.com |
Table B
| ID | TENANT | STAGE | Change_date |
|---|---|---|---|
| 114396845 | vp.com | stage1_Newbi | 12/14/20 |
| 114396845 | vp.com | stage3_B_Tool | 1/6/21 |
| 756392741 | vp.com | stage1 | 4/30/21 |
| 756392741 | vp.com | stage5_per_Tool | 7/23/21 |
| 756392741 | vp.com | stage6_Unbonused | 2/15/22 |
| 756392741 | vp.com | stage12_PreMate | 4/9/22 |
| 756392741 | vp.com | stage13_Respe | 7/18/22 |
期望结果
| ID | Point_date | TENANT | STAGE |
|---|---|---|---|
| 114396845 | 1/5/21 | vp.com | stage1_Newbi |
| 114396845 | 1/5/21 | vp.com | stage1_Newbi |
| 114396845 | 1/8/21 | vp.com | stage3_B_Tool |
| 114396845 | 1/8/21 | vp.com | stage3_B_Tool |
| 114396845 | 1/8/21 | vp.com | stage3_B_Tool |
| 114396845 | 1/8/21 | vp.com | stage3_B_Tool |
| 114396845 | 3/30/21 | vp.com | stage3_B_Tool |
| 114396845 | 4/5/21 | vp.com | stage3_B_Tool |
| 756392741 | 5/27/21 | vp.com | stage1 |
| 756392741 | 7/22/21 | vp.com | stage1 |
| 756392741 | 7/22/21 | vp.com | stage1 |
| 756392741 | 11/29/21 | vp.com | stage5_per_Tool |
| 756392741 | 11/29/21 | vp.com | stage5_per_Tool |
| 756392741 | 12/27/21 | vp.com | stage5_per_Tool |
| 756392741 | 1/24/22 | vp.com | stage5_per_Tool |
| 756392741 | 1/28/22 | vp.com | stage5_per_Tool |
| 756392741 | 1/29/22 | vp.com | stage5_per_Tool |
| 756392741 | 2/6/22 | vp.com | stage5_per_Tool |
| 756392741 | 2/8/22 | vp.com | stage5_per_Tool |
| 756392741 | 2/10/22 | vp.com | stage5_per_Tool |
| 756392741 | 2/10/22 | vp.com | stage5_per_Tool |
| 756392741 | 2/10/22 | vp.com | stage5_per_Tool |
| 756392741 | 2/14/22 | vp.com | stage6_Unbonused |
| 756392741 | 2/18/22 | vp.com | stage6_Unbonused |
| 756392741 | 2/18/22 | vp.com | stage6_Unbonused |
| 756392741 | 2/23/22 | vp.com | stage6_Unbonused |
| 756392741 | 2/23/22 | vp.com | stage6_Unbonused |
| 756392741 | 3/6/22 | vp.com | stage6_Unbonused |
| 756392741 | 3/10/22 | vp.com | stage6_Unbonused |
| 756392741 | 3/10/22 | vp.com | stage6_Unbonused |
需求说明
保留Table A的全部数据,新增来自Table B的Stage列。匹配规则为:根据Table A的Point_date,取Table B中对应ID、TENANT下该时间点的历史Stage值(即该Point_date之前或当天最近一次变更后的Stage)。
解决方案
方法1:使用LATERAL JOIN(适用于PostgreSQL、SQL Server、MySQL 8.0+)
这种写法效率较高,针对Table A的每条记录,精准匹配符合条件的最新Stage:
SELECT a.ID, a.Point_date, a.TENANT, b.STAGE FROM TableA a LEFT JOIN LATERAL ( SELECT STAGE FROM TableB b WHERE b.ID = a.ID AND b.TENANT = a.TENANT AND b.Change_date <= a.Point_date ORDER BY b.Change_date DESC LIMIT 1 ) b ON TRUE ORDER BY a.ID, a.Point_date;
方法2:使用窗口函数(通用支持窗口函数的数据库)
先对Table B的记录按ID、TENANT分组并按变更日期倒序排名,再关联Table A筛选出符合条件的最新记录:
WITH ranked_stages AS ( SELECT ID, TENANT, STAGE, Change_date, ROW_NUMBER() OVER (PARTITION BY ID, TENANT ORDER BY Change_date DESC) AS rn FROM TableB ) SELECT a.ID, a.Point_date, a.TENANT, rs.STAGE FROM TableA a LEFT JOIN ranked_stages rs ON a.ID = rs.ID AND a.TENANT = rs.TENANT AND rs.Change_date <= a.Point_date AND rs.rn = 1 ORDER BY a.ID, a.Point_date;
注意事项
- 确保
Point_date和Change_date字段为日期类型,如果是字符串格式,需要先转换为日期类型再比较,避免字符串排序导致的错误。例如:- MySQL:
STR_TO_DATE(Point_date, '%m/%d/%y') - PostgreSQL:
TO_DATE(Point_date, 'MM/DD/YY') - SQL Server:
CAST(Point_date AS DATE)
- MySQL:
- 如果存在多个相同ID、TENANT且Change_date相同的记录,
ROW_NUMBER()会随机取一条,若想保留所有符合条件的记录,可替换为RANK()。
内容的提问来源于stack exchange,提问作者SAugustine
相关产品推荐
相关产品推荐

