基于@Gordon-Linoff 的回答和他关于间隙和孤岛的线索,但我在添加测试数据时遇到了错误,正如评论中提到的那样。我也用过这个帖子
https://bertwagner.com/posts/gaps-and-islands/
-- test data
DECLARE @t TABLE (name varchar(50), orderID int, StartDate DateTime, EndDate DateTime);
INSERT INTO @t
SELECT 'Joe Smith', 1, '2020-01-01', '2020-09-30' UNION
SELECT 'Joe Smith', 2, '2020-10-01', '2020-12-30' UNION
SELECT 'Joe Smith', 3, '2021-01-01', '2021-09-30' UNION
SELECT 'Joe Smith', 4, '2021-10-01', '2021-12-31' UNION
SELECT 'Joe Smith', 5, '2022-01-01', '2022-09-30' UNION
SELECT 'Jane Doe', 6, '2020-01-01', '2020-09-30' UNION
SELECT 'Jane Doe', 7, '2020-11-01', '2020-12-30';
-- caculate the difference add 1 because EndDate is inclusive (ends on the start of next day)
SELECT t.*, d.IslandStartDate, d.IslandEndDate, DATEDIFF(DAY, IslandStartDate, IslandEndDate) + 1 AS Days FROM (
-- return the minimum and maximum start and end dates
SELECT
name,
MIN(StartDate) AS IslandStartDate,
MAX(EndDate) AS IslandEndDate
FROM (
SELECT
*,
-- indicates when a new island begins by looking if the current row's StartDate occurs after the previous row's EndDate
CASE WHEN Groups.PreviousEndDate >= DATEADD(DAY, -1, StartDate) THEN 0 ELSE 1 END AS IslandStartInd,
-- indicates which island number the current row belongs to
SUM(CASE WHEN Groups.PreviousEndDate >= DATEADD(DAY, -1, StartDate) THEN 0 ELSE 1 END) OVER (PARTITION BY name ORDER BY Groups.RN) AS IslandId
FROM
(
-- create a row number column based on the sequence of start and end dates, as well as bring the previous row's EndDate to the current row
SELECT
name,
orderID,
ROW_NUMBER() OVER(PARTITION BY name ORDER BY StartDate,EndDate) AS RN,
StartDate,
EndDate,
LAG(EndDate,1) OVER (PARTITION BY name ORDER BY StartDate, EndDate) AS PreviousEndDate
FROM
@t
) Groups
) Islands
GROUP BY
name, IslandId
) d
-- join to get the orderID back
INNER JOIN @t t ON d.name = t.name AND t.StartDate >= d.IslandStartDate AND t.EndDate <= d.IslandEndDate
ORDER BY IslandStartDate, name