如何在 SQL UPDATE 语句 WHERE 子句中读取脏值

Posted

技术标签:

【中文标题】如何在 SQL UPDATE 语句 WHERE 子句中读取脏值【英文标题】:How can I read dirty values in SQL UPDATE statement WHERE clause 【发布时间】:2021-10-18 18:16:11 【问题描述】:

假设我在两个单独的 SSMS 查询窗口中有以下查询:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
BEGIN TRANSACTION
UPDATE dbo.Jobs
SET [status] = 'Running'
OUTPUT Inserted.*
WHERE [status] = 'Waiting'
--I'm NOT committing yet
--Commit Transaction

我运行查询窗口 1(但不提交),然后运行查询窗口 2。

我希望查询窗口 2 仅更新在我开始查询 1 后插入的行(所有新记录的状态为“等待”)。但是,SQL Server 正在等待第一个查询完成,因为在更新语句中它没有读取脏值(即使它设置为 READ UNCOMMITTED);

有没有办法克服这个问题?

在我的应用程序中,我将有 2 个(或更多)进程运行它,我希望进程 2 应该能够拾取进程 1 尚未拾取的行;我不希望进程 2 需要等到进程 1 完成

【问题讨论】:

在我的应用程序中,我将有 2 个(或更多)进程运行它,我希望进程 2 应该能够拾取进程 1 尚未拾取的行;我不希望进程 2 需要等到进程 1 完成 状态列的基数是多少,有多少行可能处于任一状态? 可能每个状态都以50开头;随着时间的推移,它会变得更多 我不相信你在这里想要read uncommitted,你只需要跳过已经被事务锁定的行,你可以使用with(readpast)提示来完成 解决方案可能是变得更严格,而不是更少,并确保每个操作尽可能短。这可能意味着将现有的长时间运行的作业分解为更小的幂等步骤。 【参考方案1】:

你所要求的根本不可能。

即使在READ UNCOMMITTED(又名NOLOCK)的最低隔离级别,也必须使用X-Lock(专有)才能进行修改。换句话说,写入总是被锁定,即使读取这些行的读取没有被锁定。

因此,即使会话 2 也在 READ UNCOMMITTED 下运行,如果它想要进行修改,它也必须采用 X-Lock,这与第一个 X-Lock 不兼容。

这里的解决方案是在一个会话中执行此操作,或者立即提交。 在任何情况下,都不要持有任何时间长度的锁,因为它会导致大量阻塞链甚至死锁.

如果您只想忽略所有已插入的行,可以使用WITH (READPAST) 提示。


READ UNCOMMITTED 作为隔离级别或作为提示存在很大问题。

它可能导致从死锁到完全不正确的结果。例如,您可以读取一行两次,或者根本不读取,而根据模式的逻辑定义,应该正好只有一行。您可以阅读整页两次或根本不阅读。

由于U-Locks 未包含在UPDATEDELETE 语句中,您可能会遇到死锁。

而且您仍然会使用架构锁,因此您仍然可能会陷入同步统计信息更新或索引重建之后。

【讨论】:

以上是关于如何在 SQL UPDATE 语句 WHERE 子句中读取脏值的主要内容,如果未能解决你的问题,请参考以下文章

SQL编程之高级查询及注意事项

SQL语句update中的where条件的用法问题

9)子查询

oracle SQL查询中,如何在where中用条件语句,判断不同情况,追加不同的And条件?

Pandas 获得与 SQL 语句相同结果的 Pythonic 方式是啥:“UPDATE-LEFT JOIN - SET - WHERE”?

MySQL 子查询用法和注意事项