我想知道是否有人可以帮我解决这个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
我需要删除上表中的任何时间重叠。这可以用这个图说明:
所以我想出了这个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
但它不起作用。知道我在这里做错了什么吗?
**** **** EDIT
好的,我需要一段时间才能做到这一点。似乎是我需要它的工作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;
答案 0 :(得分: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
答案 1 :(得分:1)
就在我的脑海中,我相信Joe Celko的一本书就是一个例子。您可能会在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的区间内,所以它可以忽略不计,或者两个段的信息需要分成第三段,这就是问题变得棘手的地方。
所以这个解决方案只是假设它们可以被忽略。
编辑3 基于有关合并更新的评论
OP发布的SQLFiddle
-- 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);
以下两个更新中的任何一个都应该替换上面的两个更新。
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
)
);