例如,以下查询按照 Microsoft SQL Server (T-SQL) 中的预期工作。
UPDATE Customer
SET ContactName = Customer.City, City = Customer.ContactName;
Run Code Online (Sandbox Code Playgroud)
我想知道为什么上面的方法有效。我原以为会Customer.City
保持不变。为什么会出现这种情况?
如果您可以提供一些来源,以便我可以阅读有关这些主题的更多信息,我将不胜感激。
谢谢。
Pau*_*ite 12
SQL 标准指定更新的结果必须与在三个独立且不重叠的阶段执行的结果相同:
这称为更新阶段分离:从逻辑上讲,读取阶段在写入阶段之前完成,最后是验证阶段。
这就是更新产生如此效果的根本原因。SQL Server 必须确保数据库的最终提交状态与进行任何更改(步骤 2)之前所有读取操作(步骤 1)已完成一样。
就好像更新规范是:
UPDATE Customer
SET NEW.ContactName = OLD.City,
NEW.City = OLD.ContactName;
Run Code Online (Sandbox Code Playgroud)
如果 SQL Server在开始第二步写入之前逐字计算所有符合条件的行的新列值(基于现有列值)并将它们存储在某个位置,那么在许多情况下效率会很低。
SQL Server 意识到这一点,并且将尝试物理执行尽可能多的操作,通过引用访问旧数据,而不是制作副本(按值),并以流式处理方式逐行处理(可以做到这一点)安全。
引擎中围绕更新处理有许多额外的优化,因为它非常消耗资源。无论如何,所有优化都会保留标准中表达的语义。最终结果总是好像发生了完全的相分离。
其他数据库产品使用行版本控制 (MVCC) 来确保读取阶段始终看到更新前的数据。SQL Server 通常使用不同的安排,您可以在我的万圣节保护系列中阅读更多相关信息。内存优化表是个例外,它使用 HP 的行版本控制类型。
正如其他答案所暗示的那样,通常可以通过查看执行计划中的运算符来深入了解 SQL Server 处理更新的方式。
也就是说,执行计划不会公开理解最低级别的更新操作所需的所有细节。许多重要的优化和实施细节根本不会以这种方式暴露给用户。例如,无法判断更新运算符是否将通过引用或按值访问当前行中的值;或者更新是否将执行就地更新或先删除后插入。
它使用一堆运算符计算出它\xe2\x80\x99s 将要做什么。一个人会进行更新,但只有当它拥有了它\xe2\x80\x99s要设置的所有值时才进行更新。所以它不会\xe2\x80\x99t设置ContactName,然后弄清楚它\xe2\x80\x99s要将City设置为什么,它发现在第一行,它\xe2\x80\x99s将ContactName设置为“伦敦”和“城市”到“抢劫”,然后就这样做了。
\nI\xe2\x80\x99d 建议阅读 Itzik Ben-Gan\xe2\x80\x99s 有关 T-SQL 基础知识的书籍,以及 Kalen Delaney\xe2\x80\x99s 有关 SQL 内部原理的书籍。它们都解释了查询如何运行的基础知识。
\n