2017-03-01 53 views
0

再次遇到有關日程安排輸出的島嶼和空白問題,有些幫助將不勝感激。TSQL - 沒有重疊的連續時間表的島嶼和空白

在我的例子中,我有包含調度容器(intType = 1)和調度部分(intType = 0)的用戶調度列表,並且我被要求弄清楚如何傳遞一個連續調度而不是重疊我一直在這個系統中使用多年的風格。當前最有價值的時間表部件落入時間表容器的開始時間和結束時間內,除了一個代碼(intCode = 32)(其在任意兩個時間表容器的開始和結束處已經具有開始時間和結束時間)如下面的例子所示。

DECLARE @Schedule TABLE (
intUserID BIGINT, 
dtDate DATETIME, 
dtStart DATETIME, 
dtStop DATETIME, 
intCode INT, 
intType INT); 

INSERT INTO @Schedule 
    ([dtDate] 
    ,[intUserID] 
    ,[dtStart] 
    ,[dtStop] 
    ,[intCode] 
    ,[intType]) 
select '2017-02-23 00:00:00', 444444444444, '2017-02-23 10:00:00.000', '2017-02-23 19:00:00.000', 46, 1 
union 
select '2017-02-23 00:00:00', 444444444444, '2017-02-23 12:00:00.000', '2017-02-23 12:15:00.000', 66, 0 
union 
select '2017-02-23 00:00:00', 444444444444, '2017-02-23 12:20:00.000', '2017-02-23 12:26:00.000', 110,0 
union 
SELECT '2017-02-23 00:00:00', 444444444444, '2017-02-23 14:00:00.000', '2017-02-23 15:00:00.000', 76, 0 
union 
SELECT '2017-02-23 00:00:00', 444444444444, '2017-02-23 17:00:00.000', '2017-02-23 17:15:00.000', 66, 0 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 10:00:00.000', '2017-02-22 19:00:00.000', 46, 1 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 12:00:00.000', '2017-02-22 12:15:00.000', 66, 0 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 14:00:00.000', '2017-02-22 15:00:00.000', 76, 0 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 17:00:00.000', '2017-02-22 17:15:00.000', 66, 0 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 19:00:00.000', '2017-02-22 21:00:00.000', 32, 0 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 21:00:00.000', '2017-02-22 21:30:00.000', 46, 1 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 21:00:00.000', '2017-02-22 21:30:00.000', 59, 0 


SELECT * FROM @Schedule ORDER BY intUserID, dtDate, dtStart, intType DESC 

這讓我們看起來像這樣目前的時間表存儲數據:

intUserID dtDate   dtStart   dtStop   intCode intType 
------------ ---------------- ---------------- ---------------- ------- ------- 
444444444444 2017-02-23 00:00 2017-02-23 10:00 2017-02-23 19:00  46  1 
444444444444 2017-02-23 00:00 2017-02-23 12:00 2017-02-23 12:15  66  0 
444444444444 2017-02-23 00:00 2017-02-23 12:20 2017-02-23 12:26  110  0 
444444444444 2017-02-23 00:00 2017-02-23 14:00 2017-02-23 15:00  76  0 
444444444444 2017-02-23 00:00 2017-02-23 17:00 2017-02-23 17:15  66  0 
888888888888 2017-02-22 00:00 2017-02-22 10:00 2017-02-22 19:00  46  1 
888888888888 2017-02-22 00:00 2017-02-22 12:00 2017-02-22 12:15  66  0 
888888888888 2017-02-22 00:00 2017-02-22 14:00 2017-02-22 15:00  76  0 
888888888888 2017-02-22 00:00 2017-02-22 17:00 2017-02-22 17:15  66  0 
888888888888 2017-02-22 00:00 2017-02-22 19:00 2017-02-22 21:00  32  0 
888888888888 2017-02-22 00:00 2017-02-22 21:00 2017-02-22 21:30  46  1 
888888888888 2017-02-22 00:00 2017-02-22 21:00 2017-02-22 21:30  59  0 

了新的要求是有一個時間表容器填補了時間表,其中的原始的起始和結束時間之間的所有時間有沒有檔期的一部分,或者說看起來像這樣:

intUserID dtDate   dtStart   dtStop   intCode intType 
------------ ---------------- ---------------- ---------------- ------- ------- 
444444444444 2017-02-23 00:00 2017-02-23 10:00 2017-02-23 12:00  46  1 
444444444444 2017-02-23 00:00 2017-02-23 12:00 2017-02-23 12:15  66  0 
444444444444 2017-02-23 00:00 2017-02-23 12:15 2017-02-23 12:20  46  1 
444444444444 2017-02-23 00:00 2017-02-23 12:20 2017-02-23 12:26  110  0 
444444444444 2017-02-23 00:00 2017-02-23 12:26 2017-02-23 14:00  46  1 
444444444444 2017-02-23 00:00 2017-02-23 14:00 2017-02-23 15:00  76  0 
444444444444 2017-02-23 00:00 2017-02-23 15:00 2017-02-23 17:00  46  1 
444444444444 2017-02-23 00:00 2017-02-23 17:00 2017-02-23 17:15  66  0 
444444444444 2017-02-23 00:00 2017-02-23 17:15 2017-02-23 19:00  46  1 
888888888888 2017-02-22 00:00 2017-02-22 10:00 2017-02-22 12:00  46  1 
888888888888 2017-02-22 00:00 2017-02-22 12:00 2017-02-22 12:15  66  0 
888888888888 2017-02-22 00:00 2017-02-22 12:15 2017-02-22 14:00  46  1 
888888888888 2017-02-22 00:00 2017-02-22 14:00 2017-02-22 15:00  76  0 
888888888888 2017-02-22 00:00 2017-02-22 15:00 2017-02-22 17:00  46  1 
888888888888 2017-02-22 00:00 2017-02-22 17:00 2017-02-22 17:15  66  0 
888888888888 2017-02-22 00:00 2017-02-22 17:15 2017-02-22 19:00  46  1 
888888888888 2017-02-22 00:00 2017-02-22 19:00 2017-02-22 21:00  32  0 
888888888888 2017-02-22 00:00 2017-02-22 21:00 2017-02-22 21:30  59  0 

正如你可以看到不同的是,在第二個方案中的時間表容器變成「水」來填寫計劃部分之間的時間,邊界等於容器的原始開始和結束時間。

我希望我能夠充分解釋,或者如果不能,預期的輸出將幫助有人制定一些東西,可以幫助我在這個努力中循環所有的時間表,因爲它們有數以千計的輸出和速度是必要的。

非常感謝您提供的任何幫助!

回答

0

我認爲答案在於設定時間表的界限和填補空白,而且看起來我是對的。如果有人有興趣對我的工作進行雙重檢查,我使用的解決方案如下:

DECLARE @Schedule TABLE (
intUserID BIGINT, 
dtDate DATETIME, 
dtStart DATETIME, 
dtEnd DATETIME, 
intCode INT, 
intType INT); 

INSERT INTO @Schedule 
    ([dtDate] 
    ,[intUserID] 
    ,[dtStart] 
    ,[dtEnd] 
    ,[intCode] 
    ,[intType]) 
select '2017-02-23 00:00:00', 444444444444, '2017-02-23 10:00:00.000', '2017-02-23 19:00:00.000', 46, 1 
union 
select '2017-02-23 00:00:00', 444444444444, '2017-02-23 12:00:00.000', '2017-02-23 12:15:00.000', 66, 0 
union 
select '2017-02-23 00:00:00', 444444444444, '2017-02-23 12:20:00.000', '2017-02-23 12:26:00.000', 110,0 
union 
SELECT '2017-02-23 00:00:00', 444444444444, '2017-02-23 14:00:00.000', '2017-02-23 15:00:00.000', 76, 0 
union 
SELECT '2017-02-23 00:00:00', 444444444444, '2017-02-23 17:00:00.000', '2017-02-23 17:15:00.000', 66, 0 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 10:00:00.000', '2017-02-22 19:00:00.000', 46, 1 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 12:00:00.000', '2017-02-22 12:15:00.000', 66, 0 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 14:00:00.000', '2017-02-22 15:00:00.000', 76, 0 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 17:00:00.000', '2017-02-22 17:15:00.000', 66, 0 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 19:00:00.000', '2017-02-22 21:00:00.000', 32, 0 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 21:00:00.000', '2017-02-22 21:30:00.000', 46, 1 
union 
select '2017-02-22 00:00:00', 888888888888, '2017-02-22 21:00:00.000', '2017-02-22 21:30:00.000', 59, 0 




DECLARE @tmpSchedule TABLE (
dtDate datetime 
,dtStart datetime 
,dtEnd datetime 
,intCode int 
,intType int 
,intUserID bigint 
) 

DECLARE @tmpBaseCodes TABLE (
dtDate datetime 
,intCode int 
,intUserID bigint null 
) 

insert into @tmpSchedule 
-- Create schedule boundaries 
select dtDate, dtStart, dtStart dtEnd, intCode, intType, intUserID from @Schedule where inttype = 1 
union 
select dtDate, dtEnd dtStart, dtEnd, intCode, intType, intUserID from @Schedule where intType = 1 
union 
select dtDate, dtStart, dtEnd, intCode, intType, intUserID from @Schedule where inttype = 0 

insert into @tmpBaseCodes 
select dtDate, min(intCode) intCode, intUserID from @tmpSchedule where intType = 1 group by dtDate, intUserID; 
-- Calculate gaps and apply base code between them 
WITH C1 AS (
     SELECT dtDate, intUserID, ts, Type 
      ,e=CASE Type WHEN 1 THEN NULL ELSE ROW_NUMBER() OVER (PARTITION BY dtDate,intUserID, Type ORDER BY dtEnd) END 
      ,s=CASE Type WHEN -1 THEN NULL ELSE ROW_NUMBER() OVER (PARTITION BY dtDate,intUserID, Type ORDER BY dtStart) END 
     FROM @tmpSchedule 
     CROSS APPLY (
      VALUES (1, dtStart), (-1, dtEnd)) a(Type, ts) 
     ), 
    C2 AS (
     SELECT C1.* 
      ,se=ROW_NUMBER() OVER (PARTITION BY dtDate,intUserID ORDER BY ts, Type DESC) 
     FROM C1), 
    C3 AS (
     SELECT dtDate,intUserID, ts 
      ,grpnm=FLOOR((ROW_NUMBER() OVER (PARTITION BY dtDate, intUserID ORDER BY ts)-1)/2 + 1) 
     FROM C2 
     WHERE COALESCE(s-(se-s)-1, (se-e)-e) = 0), 
    -- C1, C2, C3, C4 combined remove the overlapping date periods 
    C4 AS (
     SELECT dtDate, intUserID, dtStart=MIN(ts), dtEnd=MAX(ts) 
     FROM C3 
     GROUP BY dtDate, intUserID, grpnm) 
INSERT INTO @tmpSchedule 
(dtdate, dtstart, dtend, intCode, intType, intUserID) 
SELECT a.dtDate, dtStart=MIN(newdate), dtEnd=MAX(newdate), b.intCode, 1 intType, a.intUserID 
FROM (
    SELECT dtDate, newdate, intUserID 
     ,rn=ROW_NUMBER() OVER (PARTITION BY dtDate,intUserID ORDER BY newdate)/2 
    FROM C4 a 
    CROSS APPLY (
     VALUES (dtStart),(dtEnd)) b(newdate) 
    ) a 
JOIN @tmpBaseCodes b on b.dtDate = a.dtDate and b.intUserID = a.intUserID 
GROUP BY a.dtDate, a.intUserID, b.intCode, rn 
HAVING COUNT(*) = 2 
ORDER BY intUserID, dtDate, dtStart; 

-- Select the entries from the schedule except boundaries 
select * from @tmpSchedule 
where dtStart != dtEnd 
order by intUserID,dtDate, dtStart, intType desc