伪代码:
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
返回的行完全相同。我知道一个解决方案是使用临时表。但我想到的问题是 - 我可以通过将事务隔离级别设置为serilized
来实现此目的吗?或者它只影响SELECT
?这里最好的解决方案是什么?
有问题的数据库是MSSQL 2012,如果它是相关的。
答案 0 :(得分:1)
在多用户环境中可能存在三个不一致问题:
脏读:事务从其他事件中读取未提交的(脏)数据 未提交的交易。
不可重复读取:后续尝试从内部读取相同数据 同一事务返回不同的结果。出现这种数据不一致问题 当其他事务修改,甚至删除时,读取之间的数据 由受影响的交易完成。
幻像读取:当后续读取内容时会发生此现象 同一事务返回新行(事务之前未读取的行)。 当另一个事务在其间插入新数据时会发生这种情况 由受影响的交易完成的读取。
此表格将显示每个交易级别可能存在的不一致情况:
+---------------+-------------+----------------------+---------------+
|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语句中有一定的值(等等50),你想要更新它10%(55)但同时有人已经打开它并且更改为100,当您提交事务时它将是110.这可以通过REPEATABLE READ轻松解决,而不使用高锁定隔离级别(Serializable),阻止您在表格上读取/插入/更新任何内容一个事务,甚至很难你只是更新一行,或者使用tempdb来保存行版本的Snapshot。
更新您的评论
使用SNAPSHOT或SERIALIZABLE将不会更新更新语句启动后添加的行,从而提高一致性。然而,这两者之间的区别在于SNAPSHOT将为您提供乐观读取,并且您仍然可以读取原始数据,而可序列化不会,它只会锁定整个对象并保留它直到事务完成。
例如,如果我们有ProdutType 1的10条记录:
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
BEGIN TRAN
Update Products
SET Qty = 45
where ProductType = 1
另一个事务插入ProductType = 1的行,它不会被提交,直到第一个事务完成,它只会影响10行。
如果不使用可重复读取锁定,您可能会获得相同的结果:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRAN
Update Products
SET Qty = 45
where ProductType = 1
我们更新了10行,并插入另一个ProductType = 1和Qty 5 - 无锁定。 提交此项将导致10个已更改的行和一行ProductType 10与Qty 5,但是如果您在该事务期间再次执行update语句,则在添加新行之后将发出11个更新