英文:
Fill missing dates given ranges of dates
问题
我有一个包含开始和结束日期的表格,如下所示:
开始时间 | 结束时间 |
---|---|
2023-05-19 20:00:00.000 | 2023-05-22 05:30:00.000 |
2023-05-22 05:30:00.000 | 2023-05-24 11:30:00.000 |
我想要一个查询,将它们拆分成如下形式:
开始时间 | 结束时间 |
---|---|
2023-05-19 20:00:00.000 | 2023-05-19 23:59:59.000 |
2023-05-20 00:00:00.000 | 2023-05-20 23:59:59.000 |
2023-05-21 00:00:00.000 | 2023-05-21 23:59:59.000 |
2023-05-22 00:00:00.000 | 2023-05-22 05:30:00.000 |
我尝试过 https://stackoverflow.com/a/57142843/22022398 但在跨越1天时不起作用。
开始时间 | 结束时间 |
---|---|
2023-05-19 20:00:00.000 | 2023-05-19 23:59:59.000 |
2023-05-22 00:00:00.000 | 2023-05-22 05:30:00.000 |
我的SQL服务器是MSSQL15,所以我不能使用generate_series()。
我还尝试过 https://stackoverflow.com/a/25911919/22022398 ,但无法弄清如何将其应用于整个表格,而不是单个日期。
英文:
I have a table full of start and end dates like
Start | End |
---|---|
2023-05-19 20:00:00.000 | 2023-05-22 05:30:00.000 |
2023-05-22 05:30:00.000 | 2023-05-24 11:30:00.000 |
I would like one select query that returns all of them split into
Start | End |
---|---|
2023-05-19 20:00:00.000 | 2023-05-19 23:59:59.000 |
2023-05-20 00:00:00.000 | 2023-05-20 23:59:59.000 |
2023-05-21 00:00:00.000 | 2023-05-21 23:59:59.000 |
2023-05-22 00:00:00.000 | 2023-05-22 05:30:00.000 |
I've tried https://stackoverflow.com/a/57142843/22022398
But it does not work when it's more than 1 day
Start | End |
---|---|
2023-05-19 20:00:00.000 | 2023-05-19 23:59:59.000 |
2023-05-22 00:00:00.000 | 2023-05-22 05:30:00.000 |
My SQL server is MSSQL15 so I can't use generate_series()
I've also tried https://stackoverflow.com/a/25911919/22022398 but can't figure out how to implement it for a whole table instead of a single date
答案1
得分: 2
以下是已翻译的代码部分:
使用递归查询,生成所有需要的日期,然后检查哪些日期保留原始日期值,哪些日期需要更新为时间 '00:00:00' 或 '23:59:59',使用窗口函数在 case 表达式内部。
WITH cte AS (
SELECT ROW_NUMBER() OVER(ORDER BY Start_) AS rn,
Start_ AS firstdate,
Start_ AS nextdate,
End_ AS lastdate
FROM tab
UNION ALL
SELECT rn, firstdate, DATEADD(dd, 1, nextdate) AS nextdate, lastdate
FROM cte
WHERE DATEADD(dd, 1, nextdate) < lastdate
)
SELECT rn,
CASE WHEN ROW_NUMBER() OVER(PARTITION BY rn ORDER BY nextdate) > 1
THEN CAST(nextdate AS DATE)
ELSE nextdate
END AS [Start],
CASE WHEN ROW_NUMBER() OVER(PARTITION BY rn ORDER BY nextdate DESC) > 1
THEN DATEADD(ss, -1, CAST(DATEADD(dd, 1, CAST(lastdate AS DATE)) AS DATETIME))
ELSE lastdate
END AS [End]
FROM cte
ORDER BY rn, [Start]
输出:
rn | 开始日期 | 结束日期 |
---|---|---|
1 | 2023-05-19 20:00:00.000 | 2023-05-22 23:59:59.000 |
1 | 2023-05-20 00:00:00.000 | 2023-05-22 23:59:59.000 |
1 | 2023-05-21 00:00:00.000 | 2023-05-22 05:30:00.000 |
2 | 2023-05-22 05:30:00.000 | 2023-05-24 23:59:59.000 |
2 | 2023-05-23 00:00:00.000 | 2023-05-24 23:59:59.000 |
2 | 2023-05-24 00:00:00.000 | 2023-05-24 11:30:00.000 |
在此处检查演示。
英文:
You can use a recursive query, that generates all and only your needed dates and then checks which are the ones to retain the original date values, and which are the ones to be updated with time '00:00:00' or '23:59:59', using window functions within case expressions.
WITH cte AS (
SELECT ROW_NUMBER() OVER(ORDER BY Start_) AS rn,
Start_ AS firstdate,
Start_ AS nextdate,
End_ AS lastdate
FROM tab
UNION ALL
SELECT rn, firstdate, DATEADD(dd, 1, nextdate) AS nextdate, lastdate
FROM cte
WHERE DATEADD(dd, 1, nextdate) < lastdate
)
SELECT rn,
CASE WHEN ROW_NUMBER() OVER(PARTITION BY rn ORDER BY nextdate) > 1
THEN CAST(nextdate AS DATE)
ELSE nextdate
END AS [Start],
CASE WHEN ROW_NUMBER() OVER(PARTITION BY rn ORDER BY nextdate DESC) > 1
THEN DATEADD(ss, -1, CAST(DATEADD(dd, 1, CAST(lastdate AS DATE)) AS DATETIME))
ELSE lastdate
END AS [End]
FROM cte
ORDER BY rn, [Start]
Output:
rn | Start | End |
---|---|---|
1 | 2023-05-19 20:00:00.000 | 2023-05-22 23:59:59.000 |
1 | 2023-05-20 00:00:00.000 | 2023-05-22 23:59:59.000 |
1 | 2023-05-21 00:00:00.000 | 2023-05-22 05:30:00.000 |
2 | 2023-05-22 05:30:00.000 | 2023-05-24 23:59:59.000 |
2 | 2023-05-23 00:00:00.000 | 2023-05-24 23:59:59.000 |
2 | 2023-05-24 00:00:00.000 | 2023-05-24 11:30:00.000 |
Check the demo here.
答案2
得分: 1
以下是翻译好的内容:
类似这样将起作用。我建议使用实际的数字表,而不是公用表达式(CTE),但这是你可以自行解决的事情。
创建临时表 #tmp(StartDT datetime, EndDT datetime)
插入到 #tmp 值
('2023-05-19 20:00:00.000', '2023-05-22 05:30:00.000'),
('2023-05-22 05:30:00.000', '2023-05-24 11:30:00.000')
;使用 NUMS 作为
(
选择顶部 365
行号() 排在第一位(按 c1.[object_id] 排序) RN
从 sys.all_columns c1
交叉连接 sys.all_columns c2
)
选择
情况 WHEN RN = 1 THEN StartDT
其他时候 CAST(DATEADD(day, RN-1,CAST(StartDT as date) ) as datetime )
结束 NewStart
,
情况 WHEN RN = DATEDIFF(day, t1.StartDT,t1.EndDT)+1
其他时候
DATEADD(second,-1, CAST(DATEADD(day, RN,CAST(StartDT as date)) as datetime))
结束 EndDT
从 #tmp t1
交叉适用(选择 RN from NUMS where RN <= DATEDIFF(day, t1.StartDT,t1.EndDT)+1) DayNums
按 NEWStart 排序
英文:
Something like this will do the trick. I would suggest using an actual numbers table instead of a CTE, but that is something you can tackle on your own.
CREATE TABLE #tmp(StartDT datetime, EndDT datetime)
INSERT INTO #tmp VALUES
('2023-05-19 20:00:00.000', '2023-05-22 05:30:00.000'),
('2023-05-22 05:30:00.000', '2023-05-24 11:30:00.000')
;WITH NUMS AS
(
SELECT TOP 365
ROW_NUMBER() OVER(ORDER BY c1.[object_id]) RN
FROM sys.all_columns c1
CROSS JOIN sys.all_columns c2
)
SELECT
CASE WHEN RN = 1 THEN StartDT
ELSE CAST(DATEADD(day, RN-1,CAST(StartDT as date) ) as datetime )
END NewStart
,
CASE WHEN RN = DATEDIFF(day, t1.StartDT,t1.EndDT)+1
THEN EndDT
ELSE
DATEADD(second,-1, CAST(DATEADD(day, RN,CAST(StartDT as date)) as datetime))
END EndDT
FROM #tmp t1
CROSS APPLY(SELECT RN from NUMS where RN <= DATEDIFF(day, t1.StartDT,t1.EndDT)+1) DayNums
ORDER BY NEWStart
答案3
得分: 0
DECLARE @table AS TABLE (
startdate DATETIME,
enddate DATETIME
);
INSERT INTO @table
(
startdate,
enddate
)
VALUES
('2023-05-01', -- startdate - datetime
'2023-05-05' -- enddate - datetime
),
('2023-05-10', -- startdate - datetime
'2023-05-15' -- enddate - datetime
);
WITH cte
AS (
SELECT
startdate,
DATEADD(SECOND, 86399, startdate) enddate,
'N' AS msg,
enddate AS newendate ---column to stop loop
FROM @table
UNION ALL
SELECT
DATEADD(SECOND, 1, enddate) startdate,
DATEADD(SECOND, 86399, DATEADD(SECOND, 1, enddate)) AS enddate,
'Y' msg,
newendate
FROM cte
WHERE startdate < cte.newendate
)
SELECT
startdate,
enddate
FROM cte
ORDER BY 1;
英文:
I think below code can help you
DECLARE @table AS TABLE (
startdate DATETIME,
enddate DATETIME
);
INSERT INTO @table
(
startdate,
enddate
)
VALUES
('2023-05-01', -- startdate - datetime
'2023-05-05' -- enddate - datetime
),
('2023-05-10', -- startdate - datetime
'2023-05-15' -- enddate - datetime
);
WITH cte
AS (
SELECT
startdate,
DATEADD(SECOND, 86399, startdate) enddate,
'N' AS msg,
enddate AS newendate ---column to stop loop
FROM @table
UNION ALL
SELECT
DATEADD(SECOND, 1, enddate) startdate,
DATEADD(SECOND, 86399, DATEADD(SECOND, 1, enddate)) AS enddate,
'Y' msg,
newendate
FROM cte
WHERE startdate < cte.newendate
)
SELECT
startdate,
enddate
FROM cte
ORDER BY 1;
答案4
得分: 0
;WITH _list as (
select
min(Start_) mins
,max(Start_) as maxs
from TestA
), listTest AS
(
select
mins as StartTime,iif(
mins=maxs
,maxs,
cast( cast( cast( mins as date) as varchar(100)) +cast( ' 23:59:59' as varchar(100)) as datetime)
)
as EndTime
,mins,maxs
from _list
UNION ALL
SELECT DATEADD(DAY,1,
cast( cast( cast( StartTime as date) as varchar(100))
+cast( ' 00:00:00' as varchar(100)) as datetime)
) AS StartTime,
IIF(
cast( DATEADD(DAY,1, StartTime ) as date)=
cast(maxs as date)
,maxs,DATEADD(DAY,1, EndTime )
) AS EndTime
,mins,maxs
FROM listTest
WHERE StartTime < (select cast( maxs as date) from _list)
)
select
StartTime
,EndTime
from listTest a
left join TestA b on cast( a.StartTime as date)=cast( b.Start_ as date)
英文:
You can Use Cte
;WITH _list as (
select
min(Start_) mins
,max(Start_) as maxs
from TestA
), listTest AS
(
select
mins as StartTime,iif(
mins=maxs
,maxs,
cast( cast( cast( mins as date) as varchar(100)) +cast( ' 23:59:59' as varchar(100)) as datetime)
)
as EndTime
,mins,maxs
from _list
UNION ALL
SELECT DATEADD(DAY,1,
cast( cast( cast( StartTime as date) as varchar(100))
+cast( ' 00:00:00' as varchar(100)) as datetime)
) AS StartTime,
IIF(
cast( DATEADD(DAY,1, StartTime ) as date)=
cast(maxs as date)
,maxs,DATEADD(DAY,1, EndTime )
) AS EndTime
,mins,maxs
FROM listTest
WHERE StartTime < (select cast( maxs as date) from _list)
)
select
StartTime
,EndTime
from listTest a
left join TestA b on cast( a.StartTime as date)=cast( b.Start_ as date)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论