【问题标题】:Selecting count of consecutives dates before and after a specified date based on start/end根据开始/结束选择指定日期之前和之后的连续日期计数
【发布时间】:2021-03-26 09:28:41
【问题描述】:

我正在尝试确定在指定日期之前和之后具有连续日期的记录数(上一条记录的结束日期与下一条记录的开始日期相同),并在出现时立即忽略任何连续记录链条断裂。

如果我有以下数据:

-- declare vars
DECLARE @dateToCheck    date = '2020-09-20'
DECLARE @numRecsBefore  int = 0 
DECLARE @numRecsAfter   int = 0
DECLARE @tempID         int

-- temp table
CREATE TABLE #dates
    (
    [idx]       INT IDENTITY(1,1),
    [startDate] DATETIME ,
    [endDate]   DATETIME,
    [prevEndDate] DATETIME
    )

-- insert temp table
INSERT INTO #dates 
        ( [startDate], [endDate] )
VALUES  ( '2020-09-01', '2020-09-04' ),
        ( '2020-09-04', '2020-09-10' ),
        ( '2020-09-10', '2020-09-16' ),
        ( '2020-09-17', '2020-09-19' ),
        ( '2020-09-19', '2020-09-20' ),
        --
        ( '2020-09-20', '2020-09-23' ),
        ( '2020-09-25', '2020-09-26' ),
        ( '2020-09-27', '2020-09-28' ),
        ( '2020-09-28', '2020-09-30' ),
        ( '2020-10-01', '2020-09-05' )

-- update with previous records endDate
DECLARE @maxRows int = (SELECT MAX(idx) FROM #dates)
DECLARE @intCount int = 0
WHILE @intCount <= @maxRows
BEGIN
    UPDATE #dates SET prevEndDate = (SELECT endDate FROM #dates WHERE idx = (@intCount - 1) ) WHERE idx=@intCount
    SET @intCount = @intCount + 1 
END

-- clear any breaks in the chain?


-- number of consecutive records before this date 
SET @numRecsBefore = (SELECT COUNT(idx) FROM #dates WHERE startDate = prevEndDate AND endDate <= @dateToCheck)

-- number of consecutive records after this date 
SET @numRecsAfter = (SELECT COUNT(idx) FROM #dates WHERE startDate = prevEndDate AND endDate >= @dateToCheck)


-- return & clean up
SELECT * FROM #dates
SELECT @numRecsBefore AS numBefore, @numRecsAfter AS numAfter
DROP TABLE #dates

由于指定日期为 '2020-09-20,我希望 @numRecsBefore = 2 和 @numRecsAfter = 1。这不是我得到的,因为它会计算所有连续记录。

必须有更好的方法来做到这一点。我知道循环不是最佳的,但我无法让 LAG() 或 LEAD() 工作。我整个上午都在尝试不同的方法和搜索,但我发现的所有内容都没有处理两个日期,或者链中的中断。

【问题讨论】:

    标签: sql sql-server datetime sql-server-2012 gaps-and-islands


    【解决方案1】:

    这听起来像是一个孤岛问题。 Islands 表示日期范围相邻的行,您想计算在同一个岛上的当前日期之后有多少条记录。

    你可以这样做:

    select 
        max(case when @dateToCheck > startdate  and @dateToCheck <= enddate then numRecsBefore end) as numRecsBefore,
        max(case when @dateToCheck >= startdate and @dateToCheck <  enddate then numRecsAfter end) as numRecsAfter
    from (
        select d.*, 
            count(*) over(partition by grp order by startdate) as numRecsBefore,
            count(*) over(partition by grp order by startdate desc) as numRecsAfter
        from (
            select d.*,
                sum(case when startdate = lag_enddate then 0 else 1 end) over(order by startdate) as grp
            from (
                select d.*,
                    lag(enddate) over(order by startdate) as lag_enddate
                from #dates d
            ) d
        ) d
    ) d
    

    这使用lag() 和累积的sum() 来定义岛屿。一个窗口计数给出了同一个岛上的数量和前后记录。最后一步是条件聚合;需要特别注意不等式以考虑各种可能性(通常,您搜索的日期可能并不总是与范围限制匹配)。

    Demo on DB Fiddle

    【讨论】:

      【解决方案2】:

      认为这就是您所追求的,但是,这并没有在您的查询中给出结果;我怀疑那是因为它们不是预期的结果?其中一个条件聚合可能还想成为&gt;=&lt;=,但我不知道是哪个:

      WITH CTE AS(
          SELECT startDate,
                 endDate,
                 CASE startDate WHEN LAG(endDate) OVER (ORDER BY startDate ASC) THEN 1 END AS IsSame
          FROM #dates d)
      SELECT COUNT(CASE WHEN startDate < @dateToCheck THEN IsSame END) AS numBefore,
             COUNT(CASE WHEN startDate > @dateToCheck THEN IsSame END) AS numAfter
      FROM CTE;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-29
        • 1970-01-01
        相关资源
        最近更新 更多