ahm*_*md0 6 sql sql-server sql-update
我想知道是否有人可以帮我解决这个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
Run Code Online (Sandbox Code Playgroud)
我需要删除上表中的任何时间重叠.这可以用这个图说明:

所以我想出了这个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
Run Code Online (Sandbox Code Playgroud)
但它不起作用.知道我在这里做错了什么吗?
****编辑****
好吧,我花了一些时间来解决这个问题.似乎是我需要它的工作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;
Run Code Online (Sandbox Code Playgroud)
我突然想到乔·塞尔科(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
)
Run Code Online (Sandbox Code Playgroud)
编辑 我忽略了页面顶部的 id 列,因此显然这是比确保端点不匹配更好的检查。如果您可以假设没有两行相同类型具有 dtIn,则解决方案可能会更容易。
顺便说一句,当子查询执行完全相同的工作时,没有理由使用 CROSS APPLY。
编辑2 我做了一些快速测试,我认为我的查询可以处理您图中的场景。在一种情况下,它可能不会执行您想要的操作。
对于给定类型,请按开始时间的顺序考虑最后两个段 S1 和 S2。S2 在 S1 之后开始,但也可以想象它在 S1 之前结束。S2 完全包含在 S1 的区间中,因此它要么可以忽略,要么需要将两个段的信息分成第三段,这就是问题变得更加棘手的地方。
所以这个解决方案只是假设它们可以被忽略。
编辑 3基于有关合并更新的评论
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);
Run Code Online (Sandbox Code Playgroud)
以下两个更新之一应取代上面两个更新。
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
)
);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
994 次 |
| 最近记录: |