Ric*_*rdo 8 t-sql sql-server sql-update
我打算UPDATE在SQL Server数据库表上运行以下语句:
UPDATE TABLE_A
SET COL_1=B.COL_1
FROM TABLE_A A
INNER JOIN TABLE_B B
ON A.KEY_1=B.KEY_1
WHERE B.COL_2 IS NOT NULL
AND A.COL_1=91216599
Run Code Online (Sandbox Code Playgroud)
错误地,我改为运行以下语句:
UPDATE TABLE_A
SET COL_1=B.COL_1
FROM TABLE_A_COPY A
INNER JOIN TABLE_B B
ON A.KEY_1=B.KEY_1
WHERE B.COL_2 is not NULL
AND A.COL_1=91216599
Run Code Online (Sandbox Code Playgroud)
请注意,在第二个语句(错误的一个)中,FROM子句指定表TABLE_A_COPY而不是TABLE_A.两个表具有完全相同的模式(即,相同的列)和相同的数据(在任何UPDATE执行之前,即).
双方TABLE_A并TABLE_A_COPY拥有约100万条记录和更新影响约500000条记录.第二个语句(错误的语句)运行了几个小时并且在第一个语句(正确的语句)运行40秒并且成功时失败.
显然,这两个语句在语法上都是正确的,但我不确定我究竟要求SQL Server对第一个语句做什么.
我的问题是:
SQL Server在第二个语句中尝试做什么?随着我的错误,我没有从指定记录之间的联动TABLE_A来TABLE_A_COPY,所以被它试图做两者之间的CROSS JOIN,然后更新每个记录TABLE_A一个极大倍?
如果要问的问题不是太宽泛,那么UPDATE在这些语句中有什么是有效的方案,其中FROM/JOIN条款中没有提到正在更新的表.为什么有人这样做?为什么SQL Server甚至会允许这样做?
我确实试图寻找我的问题的答案,但谷歌似乎认为我在问UPDATE FROM语法.
1) 之间没有联系TABLE_A,TABLE_A_COPY因此您将获得CROSS JOIN大量更新同一行。如果涉及并行执行,结果可能是不确定的:
CREATE TABLE #TABLE_A(KEY_1 INT PRIMARY KEY,COL_1 INT);\n\nCREATE TABLE #TABLE_A_COPY(KEY_1 INT PRIMARY KEY,COL_1 INT);\n\nCREATE TABLE #TABLE_B(KEY_1 INT PRIMARY KEY, COL_1 INT, COL_2 INT);\n\nINSERT INTO #TABLE_A VALUES (1,91216599),(2,91216599),(3,91216599),\n (4,91216599),(5,91216599),(6,6);\n\nINSERT INTO #TABLE_A_COPY VALUES (1,91216599),(2,91216599),(3,91216599),\n (4,91216599),(5,91216599),(6,6); \n\nINSERT INTO #TABLE_B VALUES (1,10,10),(2,20,20), (3,30,30);\n\n/*\nUPDATE #TABLE_A\nSET COL_1=B.COL_1\n--SELECT *\nFROM #TABLE_A A\nINNER JOIN #TABLE_B B\n ON A.KEY_1=B.KEY_1\nWHERE B.COL_2 IS NOT NULL\n AND A.COL_1=91216599;\n*/\n\nUPDATE #TABLE_A\nSET COL_1=B.COL_1\nFROM #TABLE_A_COPY A\nINNER JOIN #TABLE_B B\n ON A.KEY_1=B.KEY_1\nWHERE B.COL_2 is not NULL\n AND A.COL_1=91216599\n\nSELECT *\nFROM #TABLE_A;\nRun Code Online (Sandbox Code Playgroud)\n\n检查上面的代码如何TABLE_A记录KEY_1 = 6更改。
2)\nSQL ServerUPDATE FROM/DELETE FROM语法比 ANSI 标准更广泛,您遇到的问题可以简化为多次更新同一行。您UPDATE不会收到任何错误或警告:
从Let\'s deprecate UPDATE FROM!和Deprecate UPDATE FROM and DELETE FROM:
\n\n\n正确性?呸,谁在乎呢?
\n\n嗯,大多数都是这样。这就是我们测试的原因。
\n\n如果我搞乱了 SELECT 查询中的联接条件,导致第二个表中的行数过多\n 匹配,则我\xe2\x80\x99 会在测试后立即看到它,因为\n 得到的行数比预期的要多。如果我以类似的方式弄乱了 ANSI 标准 UPDATE 查询中的子查询条件,我会更快看到它,因为如果子查询返回多个值,SQL Server 将返回错误。但是,使用专有的 UPDATE FROM\n 语法,我可能会弄乱连接,并且永远不会注意到\xe2\x80\x93 如果同一行与表中的多于一行匹配,SQL Server 会愉快地一遍又一遍地更新同一行。连接表,仅保留最后一个更新的结果。并且无法知道那将是哪一行,因为这取决于所选择的查询执行计划。最坏的情况是,执行计划在单处理器开发服务器 \xe2\x80\x93 上的所有测试期间\n 恰好产生预期结果\n,然后在部署到四处理器之后\n双核生产服务器,我们宝贵的\n数据突然暴涨\xe2\x80\xa6
\n
如果您使用例如,MERGE您将收到错误指示:
\n\n\nMERGE 语句尝试多次更新或删除同一行。\n 当目标行与多个源行匹配时,就会发生这种情况。MERGE 语句不能多次更新/删除目标表的同一行。优化 ON 子句以确保目标行最多匹配一个源行,或使用 GROUP BY 子句对源行进行分组。
\n
所以你需要更加小心并检查你的代码。我也希望得到错误,但正如您在连接链接中看到的那样,这不会发生。
\n\n避免这种情况的一种方法是使用UPDATE alias,以便确保您使用参与的表FROM JOIN并且不涉及其他表:
UPDATE A\nSET COL_1=B.COL_1\nFROM #TABLE_A A\nINNER JOIN #TABLE_B B\n ON A.KEY_1=B.KEY_1\nWHERE B.COL_2 IS NOT NULL\n AND A.COL_1=91216599;\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
129 次 |
| 最近记录: |