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

想法是为日期生成有序时间,然后在第二行加入第一行,在第四行加入第三行,依此类推...