SQL 服务器将时间间隔拆分为多行
SQL Server split time interval to multiple rows
我有以下 SQL 个服务器表。
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 (https://msdn.microsoft.com/en-us/library/ms186734.aspx) 来帮助在一天内加入拆分。我使用了 UNION,但我认为您可以使用与我相同的连接逻辑,但不用 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
这里的思路是拉开始时间,有就先休息。如果没有中断,则 COALESCE 将拉取 closeTime。然后我们在一整天的休息时间联合起来。最后,我们将 closeTime 连接到最后一个中断,再次使用 COALESCE 在没有 "breakEnd.breakStart".
时使用 closeTime
这是一个 SQL Fiddler 的实际操作:http://sqlfiddle.com/#!6/5a4765/14
这是获得所需输出的查询
;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
这里是fiddlehttp://sqlfiddle.com/#!3/4076a/21
You can try this
DECLARE @Tab TABLE
(
[Date] [datetime],
[Time] [time],
[Row] int
)
DECLARE @Tab2 TABLE
(
[Date] [datetime],
[Row] int
)
INSERT INTO @Tab2
SELECT DISTINCT(workingDate) , ROW_NUMBER() OVER(ORDER BY [workingDate])
FROM workingSchedule
DECLARE @Count int;
DECLARE @Num [int];
DECLARE @Dat [datetime];
SET @Num=1;
SET @Count=(SELECT count(*) FROM @Tab2 t)
WHILE @Num<=@Count
BEGIN
SET @Dat=(SELECT [Date] FROM @Tab2 t WHERE t.[Row]=@Num)
INSERT INTO @Tab
SELECT * , ROW_NUMBER() OVER( ORDER BY Cte4.breakStart) Number FROM (
SELECT * FROM(
SELECT Cte.breakDate, Cte.breakStart FROM
(SELECT * FROM breakTable
UNION ALL
SELECT * FROM workingSchedule
)Cte
WHERE Cte.breakDate=@Dat
)Cte2
UNION ALL
SELECT * FROM(
SELECT Cte.breakDate, Cte.breakEnd FROM
(SELECT * FROM breakTable
UNION ALL
SELECT * FROM workingSchedule
)Cte
WHERE Cte.breakDate=@Dat
)Cte3
)Cte4
ORDER BY Cte4.breakStart
SET @Num=@Num+1;
END
;WITH Cte AS
(
SELECT * FROM @Tab t
WHERE t.[Row]%2=0
),
Cte2 AS
(
SELECT * FROM @Tab t
WHERE t.[Row]%2=1
)
SELECT DISTINCT Cte.[Date],Cte2.[Time] AS Start, Cte.[Time] AS [End] FROM Cte
INNER JOIN
Cte2
ON
(Cte2.[Row]+1)=Cte.[Row]
create table #answer
(workdate datetime null,
starttime time null,
endtime time null
)
declare cur cursor for (select workingDate,openTime,closeTime from workingSchedule)
declare @workingDate datetime,@openTime time,@closeTime time,@breakStart time,@breakEnd time,@breakEnd2 time,@counter int
set @counter = 0;
open cur
fetch cur into @workingDate,@openTime,@closeTime
while(@@FETCH_STATUS =0)
begin
declare cur2 cursor for select breakStart,breakEnd from breakTable where breakDate = @workingDate order by breakStart
open cur2
fetch cur2 into @breakStart,@breakEnd
set @counter = 1;
while(@@FETCH_STATUS=0)
begin
if(@counter = 1)
begin
insert into #answer values(@workingDate,@openTime,@breakStart)
end
else
begin
insert into #answer values(@workingDate,@breakEnd2,@breakStart)
end
set @breakEnd2 = @breakEnd
set @counter = @counter+1;
fetch cur2 into @breakStart,@breakEnd
end
close cur2
deallocate cur2
fetch cur into @workingDate,@openTime,@closeTime
end
close cur
deallocate cur
select * from #answer
drop table #answer
结果是:
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
;with cte as(
select *, row_number() over(partition by wd order by ot) rn
from
(select workingDate wd, openTime ot from workingSchedule
union
select workingDate, closeTime from workingSchedule
union
select breakDate, breakStart from breakTable
union
select breakDate, breakEnd from breakTable)t)
select c1.wd, c1.ot, c2.ot
from cte c1
join cte c2 on c1.wd = c2.wd and c1.rn + 1 = c2.rn and c1.rn % 2 = 1
order by c1.wd, c1.ot
想法是为日期生成有序时间,然后在第二行加入第一行,在第四行加入第三行,依此类推...
我有以下 SQL 个服务器表。
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 (https://msdn.microsoft.com/en-us/library/ms186734.aspx) 来帮助在一天内加入拆分。我使用了 UNION,但我认为您可以使用与我相同的连接逻辑,但不用 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
这里的思路是拉开始时间,有就先休息。如果没有中断,则 COALESCE 将拉取 closeTime。然后我们在一整天的休息时间联合起来。最后,我们将 closeTime 连接到最后一个中断,再次使用 COALESCE 在没有 "breakEnd.breakStart".
时使用 closeTime这是一个 SQL Fiddler 的实际操作:http://sqlfiddle.com/#!6/5a4765/14
这是获得所需输出的查询
;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
这里是fiddlehttp://sqlfiddle.com/#!3/4076a/21
You can try this
DECLARE @Tab TABLE
(
[Date] [datetime],
[Time] [time],
[Row] int
)
DECLARE @Tab2 TABLE
(
[Date] [datetime],
[Row] int
)
INSERT INTO @Tab2
SELECT DISTINCT(workingDate) , ROW_NUMBER() OVER(ORDER BY [workingDate])
FROM workingSchedule
DECLARE @Count int;
DECLARE @Num [int];
DECLARE @Dat [datetime];
SET @Num=1;
SET @Count=(SELECT count(*) FROM @Tab2 t)
WHILE @Num<=@Count
BEGIN
SET @Dat=(SELECT [Date] FROM @Tab2 t WHERE t.[Row]=@Num)
INSERT INTO @Tab
SELECT * , ROW_NUMBER() OVER( ORDER BY Cte4.breakStart) Number FROM (
SELECT * FROM(
SELECT Cte.breakDate, Cte.breakStart FROM
(SELECT * FROM breakTable
UNION ALL
SELECT * FROM workingSchedule
)Cte
WHERE Cte.breakDate=@Dat
)Cte2
UNION ALL
SELECT * FROM(
SELECT Cte.breakDate, Cte.breakEnd FROM
(SELECT * FROM breakTable
UNION ALL
SELECT * FROM workingSchedule
)Cte
WHERE Cte.breakDate=@Dat
)Cte3
)Cte4
ORDER BY Cte4.breakStart
SET @Num=@Num+1;
END
;WITH Cte AS
(
SELECT * FROM @Tab t
WHERE t.[Row]%2=0
),
Cte2 AS
(
SELECT * FROM @Tab t
WHERE t.[Row]%2=1
)
SELECT DISTINCT Cte.[Date],Cte2.[Time] AS Start, Cte.[Time] AS [End] FROM Cte
INNER JOIN
Cte2
ON
(Cte2.[Row]+1)=Cte.[Row]
create table #answer
(workdate datetime null,
starttime time null,
endtime time null
)
declare cur cursor for (select workingDate,openTime,closeTime from workingSchedule)
declare @workingDate datetime,@openTime time,@closeTime time,@breakStart time,@breakEnd time,@breakEnd2 time,@counter int
set @counter = 0;
open cur
fetch cur into @workingDate,@openTime,@closeTime
while(@@FETCH_STATUS =0)
begin
declare cur2 cursor for select breakStart,breakEnd from breakTable where breakDate = @workingDate order by breakStart
open cur2
fetch cur2 into @breakStart,@breakEnd
set @counter = 1;
while(@@FETCH_STATUS=0)
begin
if(@counter = 1)
begin
insert into #answer values(@workingDate,@openTime,@breakStart)
end
else
begin
insert into #answer values(@workingDate,@breakEnd2,@breakStart)
end
set @breakEnd2 = @breakEnd
set @counter = @counter+1;
fetch cur2 into @breakStart,@breakEnd
end
close cur2
deallocate cur2
fetch cur into @workingDate,@openTime,@closeTime
end
close cur
deallocate cur
select * from #answer
drop table #answer
结果是:
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
;with cte as(
select *, row_number() over(partition by wd order by ot) rn
from
(select workingDate wd, openTime ot from workingSchedule
union
select workingDate, closeTime from workingSchedule
union
select breakDate, breakStart from breakTable
union
select breakDate, breakEnd from breakTable)t)
select c1.wd, c1.ot, c2.ot
from cte c1
join cte c2 on c1.wd = c2.wd and c1.rn + 1 = c2.rn and c1.rn % 2 = 1
order by c1.wd, c1.ot
想法是为日期生成有序时间,然后在第二行加入第一行,在第四行加入第三行,依此类推...