根据 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 计数给出了同一岛屿上的数量和前后记录。最后一步是条件聚合;需要特别注意不等式以考虑各种可能性(通常,您搜索的日期可能并不总是与范围界限匹配)。
我认为这就是您想要的,但是,这并没有在您的查询中给出结果;我怀疑那是因为它们不是预期的结果?其中一个条件聚合可能还想成为>=
或<=
,但我不知道是哪个:
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;
我正在尝试确定在指定日期之前和之后具有连续日期(上一条记录的结束日期与下一条记录的开始日期相同的日期)的记录数,并立即忽略任何连续记录链条断了。
如果我有以下数据:
-- 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 计数给出了同一岛屿上的数量和前后记录。最后一步是条件聚合;需要特别注意不等式以考虑各种可能性(通常,您搜索的日期可能并不总是与范围界限匹配)。
我认为这就是您想要的,但是,这并没有在您的查询中给出结果;我怀疑那是因为它们不是预期的结果?其中一个条件聚合可能还想成为>=
或<=
,但我不知道是哪个:
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;