使用"开始日期"和"结束日期"将日期拆分为间隔

kkr*_*kkr 9 sql t-sql sql-server datetime sql-server-2008-r2

我有一个场景,我需要将给定的日期范围分成每月间隔.

例如,输入如下:

StartDate   EndDate
2018-01-21  2018-01-29
2018-01-30  2018-02-23
2018-02-24  2018-03-31
2018-04-01  2018-08-16
2018-08-17  2018-12-31
Run Code Online (Sandbox Code Playgroud)

预期的输出应如下所示:

StartDate   EndDate
2018-01-21  2018-01-29
2018-01-30  2018-01-31
2018-02-01  2018-02-23
2018-02-24  2018-02-28
2018-03-01  2018-03-31
2018-04-01  2018-04-30
2018-05-01  2018-05-31
2018-06-01  2018-06-30
2018-07-01  2018-07-31
2018-08-01  2018-08-16
2018-08-17  2018-08-31
2018-09-01  2018-09-30
2018-10-01  2018-10-31
2018-11-01  2018-11-30
2018-12-01  2018-12-31
Run Code Online (Sandbox Code Playgroud)

以下是示例数据.

CREATE TABLE #Dates
(
    StartDate DATE,
    EndDate DATE
);


INSERT INTO #Dates
(
    StartDate,
    EndDate
)
VALUES
('2018-01-21', '2018-01-29'),
('2018-01-30', '2018-02-23'),
('2018-02-24', '2018-03-31'),
('2018-04-01', '2018-08-16'),
('2018-08-17', '2018-12-31');
Run Code Online (Sandbox Code Playgroud)

Sal*_*n A 2

您可以使用递归 CTE。基本思想是从第一个日期开始2018-01-21,构建直到最后一个日期的所有月份的开始和结束日期的列表2018-12-31。然后与您的数据进行内部连接,并在必要时限制日期。

DECLARE @Dates TABLE (StartDate DATE, EndDate DATE);
INSERT INTO @Dates (StartDate, EndDate) VALUES
('2018-01-21', '2018-01-29'),
('2018-01-30', '2018-02-23'),
('2018-02-24', '2018-03-31'),
('2018-04-01', '2018-08-16'),
('2018-08-17', '2018-12-31');

WITH minmax AS (
    -- clamp min(start date) to 1st day of that month
    SELECT DATEADD(MONTH, DATEDIFF(MONTH, CAST('00010101' AS DATE), MIN(StartDate)), CAST('00010101' AS DATE)) AS mindate, MAX(EndDate) AS maxdate
    FROM @Dates
), months AS (
    -- calculate first and last day of each month
    -- e.g. for February 2018 it'll return 2018-02-01 and 2018-02-28
    SELECT mindate AS date01, DATEADD(DAY, -1, DATEADD(MONTH, 1, mindate)) AS date31, maxdate
    FROM minmax
    UNION ALL
    SELECT DATEADD(MONTH, 1, prev.date01), DATEADD(DAY, -1, DATEADD(MONTH, 2, prev.date01)), maxdate
    FROM months AS prev
    WHERE prev.date31 < maxdate
)
SELECT
    -- clamp start and end date to first and last day of corresponding month
    CASE WHEN StartDate < date01 THEN date01 ELSE StartDate END,
    CASE WHEN EndDate > date31 THEN date31 ELSE EndDate END
FROM months
INNER JOIN @Dates ON date31 >= StartDate AND EndDate >= date01
Run Code Online (Sandbox Code Playgroud)

如果 rCTE 不是一个选项,您可以随时加入数字表或日期表(上面的想法仍然适用)。

  • `EOMONTH` 是在 2012 年版本中引入的,我认为 IIF 也是如此。 (2认同)