use*_*701 7 sql-server cte update
我对这个 CTE 更新 stmt 有点困惑:
DECLARE @a TABLE (ID int, Value int);
DECLARE @b TABLE (ID int, Value int);
INSERT @a VALUES (1, 10), (2, 20);
INSERT @b VALUES (1, 100),(2, 200);
WITH cte AS
(
SELECT * FROM @a
)
UPDATE cte
SET Value = b.Value
FROM cte AS a
INNER JOIN @b AS b
ON b.ID = a.ID
SELECT * FROM @a
GO
Run Code Online (Sandbox Code Playgroud)
为什么这会导致表@a
的两行都为 100?我认为 ID 1 应该是 100,ID 2 应该是 200。
如果我使用表而不是公用表表达式来进行更新,我会得到预期的结果:
DECLARE @a TABLE (ID int, Value int);
DECLARE @b TABLE (ID int, Value int);
INSERT @a VALUES (1, 10), (2, 20);
INSERT @b VALUES (1, 100),(2, 200);
SELECT *
FROM @a
UPDATE @a
SET Value = b.Value
FROM @a AS XX
INNER JOIN @b AS b ON b.ID = xx.ID
SELECT *
FROM @a
Run Code Online (Sandbox Code Playgroud)
这导致表中@a
包含 100 和 200。但是我们不应该得到相同的值吗?基于之前对引用问题的解释?-- 对@a
表进行更新,而不是对引用的 XX。
RDF*_*ozz 10
(使用您的辅助查询中的信息更新)
在您的第一个查询中,UPDATE cte
说从 CTE 更新表。
FROM cte as a
说将 CTE 中的表称为a
.
所以,我们在两个地方提到了我们的 CTE。
您可能没有意识到,每次 CTE 出现在您的查询中时都会重新评估它,就像您用子查询替换了引用一样。由于您已两次单独引用 CTE,因此您已为数据库引擎生成了两个单独的结果集以供使用。
当您说使用b.Value
where 时a.ID = b.ID
,我们有两行 - 一行b.Value
是 100,另一行是 200 - 来自表b
和我们的第二个 CTE 结果集。
但是,我们正在根据这两行更新第一个CTE 结果集。因此,它会根据返回的两行更新第一个结果集中的每一行。有没有关系的两个结果之间,即使它们表示相同的底层数据。引擎CROSS JOIN
在您的连接结果和第一个结果集之间执行更新操作。
您的UPDATE
语句将您的两行都更新为 200,然后更新为 100(因为引擎决定应用交叉连接行的最快方式,它们可能不会按照输入的顺序排列)。两行都更新为相同的值,因为它们是从相同的多行更新的。
您的第一个查询在功能上等同于:
DECLARE @a TABLE (ID int, Value int);
DECLARE @b TABLE (ID int, Value int);
INSERT @a VALUES (1, 10), (2, 20);
INSERT @b VALUES (1, 100),(2, 200);
WITH cte AS
(
SELECT * FROM @a
)
UPDATE cte
SET Value = b.Value
FROM (SELECT * FROM @a) AS a
INNER JOIN @b AS b
ON b.ID = a.ID
SELECT * FROM @a
GO
Run Code Online (Sandbox Code Playgroud)
在你的第二个查询,数据库引擎知道这两个a
和@a
引用查询外的表,它知道a
和@a
意思是相同的,所以它正确地从捆绑的行@b
以@a
执行更新时。
在评论中,你问:
两者的结果总是 100 吗?或者有时两者都是 200 - 正如我所见,这里没有明确的规则?
是 100 还是 200 可能会有所不同。
我想说的是,考虑到您的第一个查询中显示的相同语句,以相同的方式执行,您几乎肯定会得到相同的结果。
然而,在现实世界中,由于表格可以看到其他活动,您无法真正确定一种结果或另一种结果,尤其是随着时间的推移。这将取决于数据库引擎如何匹配连接中的表,然后在应用更新时处理行。
用“a”别名 cte 的简单错误
您应该更新“a”而不是更新“cte”
DECLARE @a TABLE (ID int, Value int);
DECLARE @b TABLE (ID int, Value int);
INSERT @a VALUES (1, 10), (2, 20);
INSERT @b VALUES (1, 100),(2, 200);
WITH cte AS (SELECT ID, Value
FROM @a)
UPDATE a --Changed from "UPDATE cte"
SET Value = b.Value
FROM cte AS a
INNER JOIN @b AS b ON b.ID = a.ID;
SELECT * FROM @a;
ID Value
----------- -----------
1 100
2 200
(2 rows affected)
Run Code Online (Sandbox Code Playgroud)