从日期集中获取日期范围差距

时间:2019-02-11 05:13:19

标签: sql sql-server tsql sql-server-2016

这是我们的组合

Date
-----------
2018-04-01
2018-04-02
2018-04-18
2018-04-19
2018-04-21
2018-04-22
2018-04-23
2018-04-24
2018-04-25
2018-04-26
2018-04-27
2018-04-28
2018-04-29
2018-05-05
2018-05-06
2018-05-07
2018-05-08
2018-05-09
2018-05-28
2018-05-29
2018-05-30
2018-05-31

我们希望每个月缺少日期范围。所以输出将是这样的:

start_date    end_date
------------------------
2018-04-03    2018-04-17
2018-04-20    2018-04-20
2018-04-30    2018-04-30
2018-05-01    2018-05-04
2018-05-10    2018-05-27

请注意,我们缺少2018-04-302018-05-04之间的日期,但输出应分别在每个月的范围内。

谢谢。

2 个答案:

答案 0 :(得分:4)

这是一个典型的差距和孤岛问题。我选择使用相关子查询来解决这种古老的方法:

WITH cte AS (
    SELECT t1.Date, ROW_NUMBER() OVER (ORDER BY t1.Date) rn,
    FROM yourTable t1
    WHERE NOT EXISTS (SELECT TOP 1 t2.Date FROM yourTable t2
                      WHERE t2.Date = DATEADD(day, -1, t1.Date)) OR
          NOT EXISTS (SELECT TOP 1 t2.Date FROM yourTable t2
                      WHERE t2.Date = DATEADD(day, 1, t1.Date))
)

SELECT
    DATEADD(day, 1, MIN(Date)) AS start_date,
    DATEADD(day, -1, MAX(Date)) AS end_date
FROM cte
WHERE rn > 1
GROUP BY rn / 2
HAVING MIN(Date) <> MAX(Date);

Demo

答案 1 :(得分:2)

如果您只希望不拆分月份,则可以尝试以下操作。

SELECT Dateadd(day, 1, [date])       start_date, 
       Dateadd(day, tbg - 1, [date]) end_date 
FROM   (SELECT *, 
               Datediff(day, [date], Lead([date]) OVER (ORDER BY [date])) TBG 
        FROM   @Date) T 
WHERE  T.tbg > 1 

输出(不考虑重叠月份)

+------------+------------+
| start_date | end_date   |
+------------+------------+
| 2018-04-03 | 2018-04-17 |
+------------+------------+
| 2018-04-20 | 2018-04-20 |
+------------+------------+
| 2018-04-30 | 2018-05-04 |
+------------+------------+
| 2018-05-10 | 2018-05-27 |
+------------+------------+

如果您想分割月份之间重叠的行,可以尝试如下操作。

;WITH cte 
     AS (SELECT *, 
                Datediff(month, start_date, end_date) md 
         FROM   (SELECT Dateadd(day, 1, [date])       start_date, 
                        Dateadd(day, tbg - 1, [date]) end_date 
                 FROM   (SELECT *, 
                                Datediff(day, [date], Lead([date]) 
                                                        OVER ( 
                                                          ORDER BY [date])) TBG 
                         FROM   @Date) T 
                 WHERE  T.tbg > 1) t2) 
SELECT Cast(start_date AS DATE) start_date, 
       Cast(end_date AS DATE)   end_date 
FROM   (SELECT start_date, 
               end_date 
        FROM   cte C1 
        WHERE  md = 0 
        UNION ALL 
        SELECT CASE 
                 WHEN t.n = 1 THEN start_date 
                 ELSE Dateadd(mm, Datediff(mm, 0, end_date), 0) 
               END AS start_date, 
               CASE 
                 WHEN t.n = 2 THEN end_date 
                 ELSE Dateadd (dd, -1, Dateadd(mm, Datediff(mm, 0, start_date) + 
                                                   1, 0)) 
               END AS end_date 
        FROM   cte c2 
               CROSS JOIN (SELECT 1 AS n 
                           UNION 
                           SELECT 2 AS n) t 
        WHERE  md > 0) t1 
        order by start_date

Online Demo

输出

+------------+------------+
| start_date | end_date   |
+------------+------------+
| 2018-04-03 | 2018-04-17 |
+------------+------------+
| 2018-04-20 | 2018-04-20 |
+------------+------------+
| 2018-04-30 | 2018-04-30 |
+------------+------------+
| 2018-05-01 | 2018-05-04 |
+------------+------------+
| 2018-05-10 | 2018-05-27 |
+------------+------------+