SQL查询:获取客户连续活跃的最早起始日期
需求说明
现有包含CLIENT_ID、START_CONTRACT_DATE、END_CONTRACT_DATE的数据集,需要查询每个客户无中断连续活跃周期的最早起始日期——这里的连续指后续合同的起始日期与前一份合同的结束日期完全衔接(无间隔),且该周期需是当前仍在延续(或覆盖到最晚活跃状态)的连续链。
示例1
| CLIENT_ID | CONTRACT_ID | START_CONTRACT_DATE | END_CONTRACT_DATE |
|---|---|---|---|
| 2 | 5 | 01/01/2012 | 05/04/2015 |
| 2 | 6 | 05/04/2015 | 13/06/2017 |
| 2 | 7 | 13/06/2017 | 22/05/2019 |
| 2 | 8 | 22/05/2019 | 31/12/9999 |
结果:01/01/2012
所有合同完全衔接,从最早日期一直延续到活跃至今
示例2
| CLIENT_ID | CONTRACT_ID | START_CONTRACT_DATE | END_CONTRACT_DATE |
|---|---|---|---|
| 1 | 1 | 01/01/2012 | 05/04/2015 |
| 1 | 2 | 02/09/2015 | 14/01/2017 |
| 1 | 3 | 13/06/2014 | 31/12/9999 |
| 1 | 4 | 25/03/2019 | 06/08/2020 |
结果:13/06/2014
这份合同的结束日期为活跃至今,且无中断,是当前延续的连续周期起点
示例3
| CLIENT_ID | CONTRACT_ID | START_CONTRACT_DATE | END_CONTRACT_DATE |
|---|---|---|---|
| 3 | 5 | 01/01/2012 | 05/04/2015 |
| 3 | 6 | 05/04/2015 | 13/06/2017 |
| 3 | 7 | 13/06/2017 | 22/05/2018 |
| 3 | 8 | 22/05/2019 | 31/12/9999 |
结果:22/05/2019
前三个合同链在2018年结束后,2019年的合同与之前有间隔,当前延续的周期从2019年开始
解决方案
SAS Proc SQL 实现
核心思路是反向追溯:从最晚结束的合同(即END_CONTRACT_DATE = '31/12/9999',需先转换为SAS日期值)开始,不断向前找衔接的合同,直到找不到上一份衔接合同为止,最终取链中最小的起始日期。
代码如下:
proc sql; create table client_continuous_start as with recursive contract_chain as ( -- 初始步骤:取每个客户当前活跃的合同(结束日期为9999-12-31) select client_id, start_contract_date, end_contract_date from your_table_name where end_contract_date = input('31/12/9999', ddmmyy10.) union all -- 递归步骤:向前找与当前链中最早合同衔接的上一份合同 select c.client_id, c.start_contract_date, c.end_contract_date from your_table_name c inner join contract_chain cc on c.client_id = cc.client_id and c.end_contract_date = cc.start_contract_date ) select client_id, min(start_contract_date) as earliest_continuous_start format=ddmmyy10. from contract_chain group by client_id; quit;
说明:
- 替换
your_table_name为实际数据集名称; input('31/12/9999', ddmmyy10.)将字符型结束日期转为SAS日期值,确保日期比较准确;- 递归会持续向上匹配衔接合同,直到无匹配项,最终的最小起始日期即为目标值。
标准SQL 实现(适用于其他支持递归的数据库)
如果使用MySQL 8.0+、PostgreSQL、SQL Server等数据库,思路一致,仅需调整日期格式处理:
WITH RECURSIVE contract_chain AS ( -- 初始:取活跃至今的合同 SELECT client_id, start_contract_date, end_contract_date FROM your_table_name WHERE end_contract_date = '9999-12-31' UNION ALL -- 递归向前找衔接合同 SELECT c.client_id, c.start_contract_date, c.end_contract_date FROM your_table_name c JOIN contract_chain cc ON c.client_id = cc.client_id AND c.end_contract_date = cc.start_contract_date ) SELECT client_id, MIN(start_contract_date) AS earliest_continuous_start FROM contract_chain GROUP BY client_id;
内容的提问来源于stack exchange,提问作者André Cordeiro
相关产品推荐
相关产品推荐

