Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 选择/更新场景中的幻影读取_Sql_Sql Server_Transactions_Isolation Level - Fatal编程技术网

Sql 选择/更新场景中的幻影读取

Sql 选择/更新场景中的幻影读取,sql,sql-server,transactions,isolation-level,Sql,Sql Server,Transactions,Isolation Level,伪代码: var data = ExecuteMSSQLQuery( "SELECT Id FROM Table WHERE Status='not_processed'"); if(ProcessData(data)) { ExecuteMSSQLQuery( "UPDATE Table SET Status='processed' WHERE Status='not_processed'"); } 我想确保在UPDATE查询中更新的行与SELECT返回的行完全相同

伪代码:

var data = ExecuteMSSQLQuery(
  "SELECT Id FROM Table WHERE Status='not_processed'");

if(ProcessData(data))
{
    ExecuteMSSQLQuery(
      "UPDATE Table SET Status='processed' WHERE Status='not_processed'");
}
我想确保在
UPDATE
查询中更新的行与
SELECT
返回的行完全相同。我知道一个解决方案是使用临时表。但我心中的问题是——我能通过将事务隔离级别设置为
系列化
来实现这一点吗?还是只影响
SELECT
s?这里的最佳解决方案是什么


相关数据库为MSSQL 2012。

在多用户环境中可能存在三个不一致问题:

脏读:事务从其他事务读取未提交(脏)数据 未提交的交易

不可重复读取:从内部读取相同数据的后续尝试 同一事务返回不同的结果。这就产生了数据不一致的问题 当其他事务修改,甚至删除时,读取之间的数据 由受影响的事务完成

幻影读取:当在幻影中进行后续读取时,会发生此现象 同一事务返回新行(事务以前未读取的行)。 当另一个事务将新数据插入 受影响的事务完成的读取

此表将显示每个事务级别可能存在的不一致:

+---------------+-------------+----------------------+---------------+
|Isolation Level| Dirty Reads | Non-Repeatable Reads | Phantom Reads |
|Read Uncommited|      YES    |        YES           |     YES       |
|Read Commited  |      NO     |        YES           |     YEs       |
|Repeatable Read|      NO     |        NO            |     YES       |
|Serializable   |      NO     |        NO            |     NO        |
|Snapshot       |      NO     |        NO            |     NO        |
+---------------+-------------+----------------------+---------------+
在您的例子中,问题在于不可重复读取:您在select语句(etc 50)期间发现一个元素具有特定值,您希望将其更新10%(55),但同时有人已经打开它并将其更改为100,在您提交事务时,它将是110。这可以通过REPEATABLE READ轻松解决,而无需使用高锁定隔离级别(Serializable),该级别会阻止您在事务期间读取/插入/更新表上的任何内容,即使您只是更新一行,或者使用tempdb保持行版本的快照


更新你的评论

使用SNAPSHOT或SERIALIZABLE将不会更新update语句启动后添加的行,从而提高一致性。然而,这两者之间的区别在于快照将提供乐观的读取,并且您仍然可以读取原始数据,而serializable则不行,它将简单地锁定整个对象并将其保留,直到事务完成

例如,如果我们有10条ProdutType为1的记录:

SET TRANSACTION ISOLATION LEVEL SNAPSHOT
BEGIN TRAN
Update Products
SET  Qty = 45 
where ProductType = 1
另一个事务插入ProductType=1的行,在第一个事务完成之前不会提交,并且只影响10行

如果不使用repeatable read锁定,您可能会获得相同的结果:

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
    BEGIN TRAN
    Update Products
    SET  Qty = 45 
    where ProductType = 1
我们更新了10行,并插入另一行,ProductType=1,数量为5-无锁定。
提交此操作将导致10个更改行和一个ProductType 10行,数量为5,但是如果在该事务期间再次执行update语句,在添加新行后,它将发出11个更新

,逻辑上,我将从
选择
获取Id,并将使用
更新,其中Id=
这肯定是一个选项。我想我的问题更多的是关于SQL server为此目的提供的工具。当然,从编程的角度来说,用所有需要的ID生成这样一个查询并不困难。我也可以尝试使用SSIS来实现这一点。无论如何,你得到的答案肯定会丰富我的知识。我想说,你所描述的问题不是我正在解决的问题。我的问题是,当
ProcessData
运行时,有人会向表中添加新行,而我的
UPDATE
将更新比
SELECT
最初返回的行更多的行。实际上,有人会更新一行中某个字段的值,这并没有问题。我明白了..快照或可序列化正如您所说的,只需确保您测试这两个字段,以查看使用可序列化的并发性在多大程度上退化,以及使用Snapshot对tempdb的影响有多大,但实际上这两个字段中的一个会起到关键作用,serializable和snapshot都锁定了我在
中指定的整个范围,从而确保
UPDATE
将看到与
SELECT
返回的结果相同的所有结果。不返回可能符合条件的新行。谢谢如果您更新您的答案以反映这一点,那么我将能够将其标记为已接受:)