【问题标题】:SQL 2012 Compare dates across several rowsSQL 2012 比较多行的日期
【发布时间】:2017-02-21 10:39:33
【问题描述】:

我有一个问题,我需要比较几行的日期。要求是数据需要按 'Region/Area' 组合以最低的 'StartDate' 和最高的 'EndDate' 分组,除非前一个 'EndDate' 和下一个 'StartDate' 之间的间隔超过 1 天。

“StartDate”始终是该月的第一天,“EndDate”始终是该月的最后一天。

给定一个这样的简化表:

Region | Area |   StartDate   |   EndDate
-------|------|---------------|-------------
   A   |   1  |   01/01/2016  |  03/31/2016
   A   |   1  |   04/01/2016  |  05/31/2016
   A   |   1  |   07/01/2016  |  09/30/2016
   A   |   1  |   10/01/2016  |  01/31/2017
   A   |   1  |   02/01/2017  |  12/31/2017
   B   |   2  |   01/01/2016  |  04/30/2016
   B   |   2  |   05/01/2016  |  09/30/2016
   A   |   4  |   01/01/2016  |  05/31/2016
   A   |   4  |   06/01/2016  |  12/31/2016

我需要结果看起来像这样:

Region | Area |   StartDate  |  EndDate  
-------|------|--------------|-----------
   A   |   1  |  01/01/2016  |  05/31/2016
   A   |   1  |  07/01/2016  |  12/31/2017
   B   |   2  |  01/01/2016  |  09/30/2016
   A   |   4  |  01/01/2016  |  12/31/2016

我已尝试使用 MIN 和 MAX 日期进行 GROUP BY,但我似乎无法正确理解它的逻辑。

任何想法或建议将不胜感激。

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    这似乎是一个数据孤岛问题。您可以使用 SQL Server 2012 中引入的窗口函数。使用 LAG 窗口函数,您可以确定您的最后记录结束日期时间是否与当前记录开始日期时间相差一天以上。接下来,您可以使用 SUM OVER 子句为每个数据岛生成分组 ID。

    DECLARE @SourceData TABLE
    (
         Region         NVARCHAR(10)
        ,Area           INT
        ,StartDate      DATETIME
        ,EndDate        DATETIME
    );
    
    INSERT INTO @SourceData
    VALUES
    ('A', 1, '01/01/2016', '03/31/2016'),
    ('A', 1, '04/01/2016', '05/31/2016'),
    ('A', 1, '07/01/2016', '09/30/2016'),
    ('A', 1, '10/01/2016', '01/31/2017'),
    ('A', 1, '02/01/2017', '12/31/2017'),
    ('B', 2, '01/01/2016', '04/30/2016'),
    ('B', 2, '05/01/2016', '09/30/2016'),
    ('A', 4, '01/01/2016', '05/31/2016'),
    ('A', 4, '06/01/2016', '12/31/2016');
    
    ;WITH CTE_DataIslands  -- First CTE determine the start of each new data island
    AS
    (
        SELECT           Region
                        ,Area
                        ,StartDate
                        ,EndDate
                        ,(
                            CASE
                                WHEN DATEADD(DAY, 1, LAG(EndDate, 1) OVER  (PARTITION BY Region, Area ORDER BY StartDate ASC)) < (StartDate) THEN 1 -- If prev record's end date + 1 day  is not equal to current record's start date then it is the start of a new data island.
                                ELSE 0
                            END
                         ) AS [IsNewDataIsland]
        FROM            @SourceData 
    )
    , CTE_GenerateGroupingID
    AS
    (
        SELECT  Region
                ,Area
                ,StartDate
                ,EndDate
                ,SUM([IsNewDataIsland]) OVER (PARTITION BY Region, Area ORDER BY StartDate ASC ROWS UNBOUNDED PRECEDING) AS GroupingID  -- Create a running total of the IsNewDataIsland column this will create a grouping id we can now group on
        FROM    CTE_DataIslands
    )
    SELECT      Region  
                ,Area
                ,MIN(StartDate) AS StartDate
                ,MAX(EndDate) AS StartDate
    FROM        CTE_GenerateGroupingID
    GROUP BY    Region, Area, GroupingID
    

    【讨论】:

    • 非常感谢,我有一些我似乎无法将它们组合在一起的部分
    • 一个后续问题 - 如果有第三个日期列,比如“ChangeDate”,我只需要每个分组的 MAX 值;你怎么把它包括在内?当我尝试时,它会不断增加结果集
    • NM,我几乎一打字就想通了。再次感谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多