如何选取两日期区间内的行并包含每行的前后行(若存在)
解决方案:筛选目标日期区间行及相邻行
嘿,这个需求用窗口函数就能完美解决!核心思路是先给每行标记是否在目标日期范围内,同时拿到同一用户下的前一行和后一行日期,最后筛选出「目标行」「目标行的前一行」「目标行的后一行」这三类数据。
原数据表
| ID | PersonID | Date |
|---|---|---|
| 1 | 1 | 2017-04-01 |
| 2 | 1 | 2017-04-15 |
| 3 | 1 | 2017-05-13 |
| 4 | 1 | 2017-06-15 |
| 5 | 1 | 2017-08-13 |
| 6 | 1 | 2017-10-02 |
| 7 | 2 | 2017-05-04 |
| 8 | 2 | 2017-09-16 |
| 9 | 3 | 2017-04-23 |
| 10 | 3 | 2017-07-06 |
| 11 | 4 | 2017-06-01 |
期望结果表
| ID | PersonID | Date |
|---|---|---|
| 2 | 1 | 2017-04-15 |
| 3 | 1 | 2017-05-13 |
| 4 | 1 | 2017-06-15 |
| 5 | 1 | 2017-08-13 |
| 6 | 1 | 2017-10-02 |
| 7 | 2 | 2017-05-04 |
| 8 | 2 | 2017-09-16 |
| 9 | 3 | 2017-04-23 |
| 10 | 3 | 2017-07-06 |
| 11 | 4 | 2017-06-01 |
实现SQL
WITH marked_records AS ( SELECT ID, PersonID, Date, -- 标记当前行是否在目标日期区间内 CASE WHEN Date BETWEEN '2017-05-01' AND '2017-08-26' THEN 1 ELSE 0 END AS is_target_row, -- 获取同一用户的上一行日期 LAG(Date) OVER (PARTITION BY PersonID ORDER BY Date) AS prev_row_date, -- 获取同一用户的下一行日期 LEAD(Date) OVER (PARTITION BY PersonID ORDER BY Date) AS next_row_date FROM your_table_name -- 替换成你的实际表名 ) SELECT ID, PersonID, Date FROM marked_records WHERE -- 条件1:当前行本身在目标区间内 is_target_row = 1 -- 条件2:当前行的下一行在目标区间内(即当前行是目标行的前一行) OR next_row_date BETWEEN '2017-05-01' AND '2017-08-26' -- 条件3:当前行的上一行在目标区间内(即当前行是目标行的后一行) OR prev_row_date BETWEEN '2017-05-01' AND '2017-08-26' ORDER BY PersonID, Date;
逻辑解释
- CTE标记阶段:用
LAG和LEAD窗口函数,按PersonID分组、Date排序,拿到每行的前后行日期;同时用CASE标记当前行是否在目标日期区间。 - 筛选阶段:只要满足三个条件中的任意一个,就保留该行:
- 当前行是目标区间内的行
- 当前行的下一行是目标行(需要把目标行的前一行也带上)
- 当前行的上一行是目标行(需要把目标行的后一行也带上)
这样就能精准得到你想要的结果啦~
内容的提问来源于stack exchange,提问作者mhsankar
相关产品推荐
相关产品推荐

