根据 start/end 选择指定日期前后连续日期的计数

Selecting count of consecutives dates before and after a specified date based on start/end

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

如果我有以下数据:

-- 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() 工作。我花了整个上午尝试不同的方法和搜索,但我发现的所有内容都不涉及两个日期,或者链中的中断。

这看起来像是一个缺口和孤岛问题。 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() 来定义岛屿。 a window 计数给出了同一岛屿上的数量和前后记录。最后一步是条件聚合;需要特别注意不等式以考虑各种可能性(通常,您搜索的日期可能并不总是与范围界限匹配)。

Demo on DB Fiddle

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

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;