2012-07-19 74 views
6

我想知道是否有人可以幫助我使用此SQL語句嗎?t-SQL更新表以刪除重疊的時間範圍

說,我有一個SQL Server 2008中的表是這樣的:

id -- INT PRIMARY KEY 
dtIn -- DATETIME2 
dtOut -- DATETIME2 
type -- INT 

id dtIn dtOut type 
1 05:00 10:00 1 
2 08:00 16:00 2 
3 02:00 08:00 1 
4 07:30 11:00 1 
5 07:00 12:00 2 

我需要刪除的任何時間上表重疊。這可以用這個圖來說明: enter image description here

所以我想出了這個SQL:

UPDATE [table] AS t 
SET dtOut = (SELECT MIN(dtIn) FROM [table] WHERE type = t.type AND t.dtIn >= dtIn AND t.dtIn < dtOut) 
WHERE type = t.type AND t.dtIn >= dtIn AND t.dtIn < dtOut 

但它不工作。任何想法我在這裏做錯了什麼?

**** ****編輯

OK,我花了一段時間去這一點。似乎是什麼,我需要它的工作SQL:

--BEGIN TRANSACTION; 

--delete identical dtIn 
DELETE dT1 
FROM tbl dT1 
WHERE EXISTS 
(
    SELECT * 
    FROM tbl dT2 
    WHERE dT1.Type = dT2.Type 
    AND dT1.dtIn = dT2.dtIn 
    AND (
      dT1.dtOut < dT2.dtOut 
      OR (dT1.dtOut = dT2.dtOut AND dT1.id < dT2.id) 
     ) 
); 

--adjust dtOuts to the max dates for overlapping section 
UPDATE tbl 
SET dtOut = COALESCE((
    SELECT MAX(dtOut) 
    FROM tbl as t1 
    WHERE t1.type = tbl.type 
    AND t1.dtIn < tbl.dtOut 
AND t1.dtOut > tbl.dtIn 
    ), dtOut); 

-- Do the actual updates of dtOut 
UPDATE tbl 
SET dtOut = COALESCE((
    SELECT MIN(dtIn) 
    FROM tbl as t2 
    WHERE t2.type = tbl.type AND 
      t2.id <> tbl.id AND 
      t2.dtIn >= tbl.dtIn AND t2.dtIn < tbl.dtOut 
    ), dtOut); 

--COMMIT TRANSACTION; 

回答

1

剛剛送走我的頭頂,我認爲,喬·塞科的一本書曾以此爲例問題。您可能會在Google上找到摘錄。

這可能更接近。我認爲你並沒有以正確的方式進行子查詢。

UPDATE table 
SET dtOut = (
    SELECT MIN(t2.dtIn) 
    FROM [table] as t2 
    WHERE t2.id <> table.id AND t2.type = table.type 
     AND table.dtIn < t2.dtIn AND t2.dtIn < table.dtOut 
     AND table.dtOut <= t2.dtOut 
    ) 
WHERE EXISTS (
    SELECT 1 
    FROM [table] as t3 
    WHERE 
      t3.type = table.type 
     AND t3.id <> table.id 
     AND table.dtIn < t3.dtIn AND t3.dtIn < table.dtOut 
     AND table.dtOut <= t3.dtOut 
    ) 

編輯 我忽視了在頁面頂部的id列如此明顯,這比確保端點不匹配的更好的檢查。如果您可以假設沒有兩行相同類型的dtIn,解決方案可能更容易。

順便說一句,當子查詢將完成相同的工作時,沒有理由使用CROSS APPLY。

編輯2 我做了一些快速測試,我認爲我的查詢處理圖中的場景。有一種情況可能不會做你想做的事。

對於給定類型,請按照開始時間順序考慮最後兩個段S1和S2。 S2在S1之後開始,但也想象它在S1之前結束。 S2完全包含在S1的區間中,因此它可以忽略,或者兩個區段的信息需要分成第三個區段,這就是問題變得棘手的地方。

所以這個解決方案只是假設他們可以被忽略。


基於註釋有關OP

發佈
-- eliminate redundant rows 
DELETE dT1 /* FROM tbl dT1 -- unnecessary */ 
WHERE EXISTS 
(
    SELECT * 
    FROM tbl dT2 
    WHERE dT1.Type = dT2.Type AND dT1.dtIn = dT2.dtIn 
    AND (
     dT1.dtOut < dT2.dtOut 
     OR (dT1.dtOut = dT2.dtOut AND dT1.id < dT2.id) 
    ) 
); 

--adjust dtOuts to the max dates 
UPDATE tbl 
SET dtOut = COALESCE((
    SELECT MAX(dtOut) 
    FROM tbl as t1 
    WHERE t1.type = tbl.type 
    ), dtOut); 

-- Do the actual updates of dtOut 
UPDATE tbl 
SET dtOut = COALESCE((
    SELECT MIN(dtIn) 
    FROM tbl as t2 
    WHERE t2.type = tbl.type AND 
      t2.id <> tbl.id AND 
      t2.dtIn >= tbl.dtIn AND t2.dtIn < tbl.dtOut 
    ), dtOut); 

無論是兩次更新之一以下應更換上述兩個更新組合更新

SQLFiddle編輯3

UPDATE tbl 
SET dtOut = (
    SELECT 
     COALESCE(
      MIN(dtIn), 
      /* as long as there's no GROUP BY, there's always one row */ 
      (SELECT MAX(dtOut) FROM tbl as tmax WHERE tmax.type = tbl.type) 
     ) 
    FROM tbl as tmin 
    WHERE tmin.type = tbl.type 

     AND tmin.dtIn > tbl.dtIn 
     /* 
     regarding the original condition in the second update: 
      t2.dtIn >= tbl.dtIn AND t2.dtIn < tbl.dtOut 

     dtIns can't be equal because you already deleted those 
     and if dtIn was guaranteed to be less than dtOut it's 
     also automatically always less than max(dtOut) 
     */ 
); 

UPDATE tbl 
SET dtOut = COALESCE(
    (
    SELECT MIN(dtIn) FROM tbl as tmin 
    WHERE tmin.type = tbl.type AND tmin.dtIn > tbl.dtIn 
), 
    (  
    SELECT MAX(dtOut) FROM tbl as tmax 
    WHERE tmax.type = tbl.type 
) 
); 
+0

感謝。我需要嘗試一下。僅僅從好奇心,什麼書和它在哪裏是你提到的代碼參考? – ahmd0 2012-07-19 22:55:26

+0

我正在考慮Joe Celko的SQL for Smarties:Advanced SQL Programming的第29章。我可以看到目錄而不是章節。 – shawnt00 2012-07-19 23:05:49

+0

對於同一行檢查,是否更容易檢查id列? – ahmd0 2012-07-19 23:06:04

2

我想CROSS APPLY可能做的伎倆:

DECLARE @T TABLE (ID INT, DTIn DATETIME2, dtOut DATETIME2, Type INT) 
INSERT @T VALUES 
(1, '05:00', '10:00', 1), 
(2, '08:00', '16:00', 2), 
(3, '02:00', '08:00', 1), 
(4, '07:30', '11:00', 1), 
(5, '07:00', '12:00', 2) 

UPDATE @T 
SET  DtOut = T3.DtOut 
FROM @T T1 
     CROSS APPLY 
     ( SELECT MIN(DtIn) [DtOut] 
      FROM @T T2 
      WHERE T2.Type = T1.Type 
      AND  T2.DtIn > T1.dtIn 
      AND  T2.DtIn < T1.dtOut 
     ) T3 
WHERE T3.dtOut IS NOT NULL 

SELECT * 
FROM @T 
+0

有趣。我不清楚 - 最後一個SELECT * FROM @T是什麼? – ahmd0 2012-07-19 23:21:34

+0

我剛剛從測試中顯示數據。它對更新聲明沒有影響。 – GarethD 2012-07-19 23:23:16