2012-07-19 76 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