简体   繁体   English

t-SQL更新表以删除重叠的时间帧

[英]t-SQL to update table to remove overlapping time frames

I was wondering if someone could help me with this SQL statement? 我想知道是否有人可以帮我解决这个SQL语句?

Say, I have an SQL Server 2008 table like this: 说,我有一个像这样的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

I need to remove any time overlaps in the table above. 我需要删除上表中的任何时间重叠。 This can be illustrated with this diagram: 这可以用这个图说明: 在此输入图像描述

So I came up with this SQL: 所以我想出了这个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

But it doesn't work. 但它不起作用。 Any idea what am I doing wrong here? 知道我在这里做错了什么吗?

****EDIT**** ****编辑****

OK, it took me awhile to get to this. 好吧,我花了一些时间来解决这个问题。 Seems to be a working SQL for what I need it for: 似乎是我需要它的工作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;

I think CROSS APPLY might do the trick: 我认为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

Just off the top of my head I believe that one of Joe Celko's books had this as an example problem. 就在我的脑海中,我相信Joe Celko的一本书就是一个例子。 You might find the excerpt available on Google. 您可能会在Google上找到摘录。

This might be closer. 这可能更接近。 I think you weren't really doing the subquery the right way. 我认为你并没有以正确的方式进行子查询。

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
    )

EDIT I overlooked the id column at the top of the page so obviously that's a better check than making sure the endpoints don't match up. 编辑我忽略了页面顶部的id列,所以显然这比确保端点不匹配更好。 The solution is probably easier if you can assume that no two rows of identical type have the dtIn. 如果您可以假设没有两行相同类型的dtIn,则解决方案可能更容易。

Btw, there's no reason to use a CROSS APPLY when a subquery will do exactly the same job. 顺便说一句,当子查询完成相同的工作时,没有理由使用CROSS APPLY。

EDIT 2 I did some quick testing and I think my query handles the scenario in your diagram. 编辑2我做了一些快速测试,我认为我的查询处理图表中的场景。 There's one case where it might not do what you want. 有一种情况可能无法满足您的需求。

For a given type, think about the final two segments S1 and S2 in order of start time. 对于给定类型,按照开始时间的顺序考虑最后两个段S1和S2。 S2 starts after S1 but also imagine it ends before S1 does. S2在S1之后开始,但也想象它在S1之前结束。 S2 is fully contained in the interval of S1, so it's either ignorable or the information for the two segments needs to be split into a third segment and that's where the problem gets trickier. S2完全包含在S1的间隔中,因此它可以是可忽略的,或者两个段的信息需要分成第三个段,这就是问题变得棘手的地方。

So this solution just assumes they can be ignored. 所以这个解决方案只是假设它们可以被忽略。


EDIT 3 based on the comment about combining updates 编辑3基于关于组合更新的评论

SQLFiddle posted by OP SQLFiddle由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);

Either one of the two updates below should replace the two updates above. 以下两个更新中的任何一个都应该替换上面的两个更新。

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
  )
);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM