SQL Server将时间间隔拆分为多行
我有以下SQL Server表SQL Server将时间间隔拆分为多行,sql,sql-server,time,split,Sql,Sql Server,Time,Split,我有以下SQL Server表 CREATE TABLE workingSchedule ( [workingDate] DATETIME NULL, [openTime] TIME (7) NULL, [closeTime] TIME (7) NULL ); INSERT INTO workingSchedule (workingDate, openTime, closeTime) VALUES ('10/1/2015','
CREATE TABLE workingSchedule
(
[workingDate] DATETIME NULL,
[openTime] TIME (7) NULL,
[closeTime] TIME (7) NULL
);
INSERT INTO workingSchedule
(workingDate, openTime, closeTime)
VALUES
('10/1/2015','9:00','17:00'),
('10/2/2015','9:00','17:00');
CREATE TABLE breakTable
(
[breakDate] DATETIME NULL,
[breakStart] TIME NULL,
[breakEnd] TIME NULL
);
INSERT INTO breakTable
(breakDate, breakStart, breakEnd)
VALUES
('10/1/2015','12:00','13:00'),
('10/1/2015','15:00','15:30'),
('10/2/2015','12:00','13:00');
考虑到[breakTable],我试图将[workingSchedule]中的时间间隔分成几行。我想要的结果如下所示:
Date Start End
10/1/2015 09:00 12:00
10/1/2015 13:00 15:00
10/1/2015 15:30 17:00
10/2/2015 09:00 12:00
10/2/2015 13:00 17:00
我不确定是否应该使用CTE、函数或临时表。如果您能分享您的解决方案代码,我将不胜感激。我可以在只有一次休息的情况下分割时间间隔,但在每天多个休息时间的情况下,我失败了。在这种情况下,我使用ROW_NUMBER来帮助在一天内加入分割。我使用了一个UNION,但我认为您可以使用与我相同的join逻辑,但可以使用SELECT语句
SELECT workingDate as [date], openTime as [Start], COALESCE(breakStart, closeTime) as [End]
FROM workingSchedule
LEFT JOIN (
SELECT breakDate, breakStart, breakEnd, ROW_NUMBER() OVER (PARTITION BY breakDate ORDER BY breakStart) AS ROWNUM
FROM breakTable
) as firstBreak ON workingSchedule.workingDate = firstBreak.breakDate AND firstBreak.ROWNUM = 1
UNION
SELECT breakStart.breakDate, breakStart.breakEnd, coalesce(breakEnd.breakStart, endTime.closeTime)
FROM (
SELECT breakDate, breakStart, breakEnd, ROW_NUMBER() OVER (PARTITION BY breakDate ORDER BY breakStart) AS ROWNUM
FROM breakTable
) as breakStart
LEFT JOIN (
SELECT breakDate, breakStart, breakEnd, ROW_NUMBER() OVER (PARTITION BY breakDate ORDER BY breakStart) AS ROWNUM
FROM breakTable
) as breakEnd ON breakStart.breakDate = breakEnd.breakDate AND breakStart.ROWNUM = breakEnd.ROWNUM - 1
LEFT JOIN (
SELECT workingDate, closeTime
FROM workingSchedule
) AS endTime ON breakStart.breakDate = endTime.workingDate
这里的想法是拉开始时间,如果有第一次突破的话。如果没有中断,则合并将拉动关闭时间。然后我们在一天的休息时间结合。最后,我们将closeTime加入到最后一个break中,当没有breakEnd.breakStart时,再次使用COALESCE来使用closeTime
这里有一个SQL的提琴手在起作用:在这样的情况下,我使用行数来帮助在一天内加入拆分。我使用了一个UNION,但我认为您可以使用与我相同的join逻辑,但可以使用SELECT语句
SELECT workingDate as [date], openTime as [Start], COALESCE(breakStart, closeTime) as [End]
FROM workingSchedule
LEFT JOIN (
SELECT breakDate, breakStart, breakEnd, ROW_NUMBER() OVER (PARTITION BY breakDate ORDER BY breakStart) AS ROWNUM
FROM breakTable
) as firstBreak ON workingSchedule.workingDate = firstBreak.breakDate AND firstBreak.ROWNUM = 1
UNION
SELECT breakStart.breakDate, breakStart.breakEnd, coalesce(breakEnd.breakStart, endTime.closeTime)
FROM (
SELECT breakDate, breakStart, breakEnd, ROW_NUMBER() OVER (PARTITION BY breakDate ORDER BY breakStart) AS ROWNUM
FROM breakTable
) as breakStart
LEFT JOIN (
SELECT breakDate, breakStart, breakEnd, ROW_NUMBER() OVER (PARTITION BY breakDate ORDER BY breakStart) AS ROWNUM
FROM breakTable
) as breakEnd ON breakStart.breakDate = breakEnd.breakDate AND breakStart.ROWNUM = breakEnd.ROWNUM - 1
LEFT JOIN (
SELECT workingDate, closeTime
FROM workingSchedule
) AS endTime ON breakStart.breakDate = endTime.workingDate
这里的想法是拉开始时间,如果有第一次突破的话。如果没有中断,则合并将拉动关闭时间。然后我们在一天的休息时间结合。最后,我们将closeTime加入到最后一个break中,当没有breakEnd.breakStart时,再次使用COALESCE来使用closeTime
这里有一个SQL的Fiddler在运行:这里是获取所需输出的查询
;WITH CTEOrderedBreaks
AS
(
SELECT row_number() over(partition by breakdate order by breakdate,breakstart asc) as rnob,
row_number() over(partition by breakdate order by breakdate,breakstart DESC) as rnobr,
breakdate,
breakstart,
breakend
from breakTable
),
CTESUMMARY
AS
(
SELECT WS.WORKINGDATE,WS.OPENTIME AS [START],COB.BREAKSTART AS [END]
FROM CTEOrderedBreaks COB INNER JOIN WORKINGSCHEDULE WS ON WS.WORKINGDATE = COB.breakdate
WHERE COB.RNOB=1
UNION ALL
SELECT COB1.BREAKDATE,COB1.BREAKEND AS [START],ISNULL(COB2.BREAKSTART,WS1.CLOSETIME)AS [END]
FROM CTEOrderedBreaks COB1
left JOIN CTEOrderedBreaks COB2 ON COB1.RNOB = COB2.RNOB-1
AND COB1.BREAKDATE = COB2.BREAKDATE
LEFT JOIN workingSchedule WS1 ON WS1.WORKINGDATE = COB1.BREAKDATE AND COB1.RNOBR=1
)
SELECT * FROM CTESUMMARY
ORDER BY WORKINGDATE,START
这里是小提琴这里是获取所需输出的查询
;WITH CTEOrderedBreaks
AS
(
SELECT row_number() over(partition by breakdate order by breakdate,breakstart asc) as rnob,
row_number() over(partition by breakdate order by breakdate,breakstart DESC) as rnobr,
breakdate,
breakstart,
breakend
from breakTable
),
CTESUMMARY
AS
(
SELECT WS.WORKINGDATE,WS.OPENTIME AS [START],COB.BREAKSTART AS [END]
FROM CTEOrderedBreaks COB INNER JOIN WORKINGSCHEDULE WS ON WS.WORKINGDATE = COB.breakdate
WHERE COB.RNOB=1
UNION ALL
SELECT COB1.BREAKDATE,COB1.BREAKEND AS [START],ISNULL(COB2.BREAKSTART,WS1.CLOSETIME)AS [END]
FROM CTEOrderedBreaks COB1
left JOIN CTEOrderedBreaks COB2 ON COB1.RNOB = COB2.RNOB-1
AND COB1.BREAKDATE = COB2.BREAKDATE
LEFT JOIN workingSchedule WS1 ON WS1.WORKINGDATE = COB1.BREAKDATE AND COB1.RNOBR=1
)
SELECT * FROM CTESUMMARY
ORDER BY WORKINGDATE,START
这是小提琴
结果是:
2015-10-01 00:00:00.000 09:00:00.0000000 12:00:00.0000000
2015-10-01 00:00:00.000 13:00:00.0000000 15:00:00.0000000
2015-10-02 00:00:00.000 09:00:00.0000000 12:00:00.0000000
结果是:
2015-10-01 00:00:00.000 09:00:00.0000000 12:00:00.0000000
2015-10-01 00:00:00.000 13:00:00.0000000 15:00:00.0000000
2015-10-02 00:00:00.000 09:00:00.0000000 12:00:00.0000000
其思想是为日期生成有序的时间,然后在第二行连接第一行,在第四行连接第三行等等
想法是为日期生成预定时间,然后在第二行加入第一行,在第四行加入第三行等等。非常感谢!这完全符合我的问题。当我尝试在一天中增加额外的休息时间时,结果正如预期的那样。非常感谢!这完全符合我的问题。当我尝试在一天中增加额外的休息时间时,结果正如预期的那样。非常感谢!这对我的问题也很有效。这种方法也很好。我很高兴能提供帮助!:如果你有一天没有休息,这将不起作用。非常感谢!这对我的问题也很有效。这种方法也很好。我很高兴能提供帮助!:如果你有一天没有休息,这是行不通的。如果你有两次休息,一次开始,另一次结束,这是不正确的。如果你有两次休息,一次开始,另一次结束,这是不正确的。