我有一个程序连接到Oracle数据库并对其执行操作。我现在想要调整该程序以支持SQL Server数据库。
在Oracle版本中,我使用“SELECT FOR UPDATE WAIT”来锁定我需要的特定行。我在更新基于SELECT的结果的情况下使用它,而其他会话绝对不能同时修改它,所以他们必须先手动锁定它。系统很可能会同时尝试访问相同的数据。
例如:
两个用户尝试获取具有最高优先级的数据库中的行,将其标记为忙,对其执行操作,并将其标记为可用以供以后使用。
在Oracle中,逻辑基本上就是这样:
BEGIN TRANSACTION;
SELECT ITEM_ID FROM TABLE_ITEM WHERE ITEM_PRIORITY > 10 AND ITEM_CATEGORY = 'CT1'
ITEM_STATUS = 'available' AND ROWNUM = 1 FOR UPDATE WAIT 5;
UPDATE [locked item_id] SET ITEM_STATUS = 'unavailable';
COMMIT TRANSACTION;
请注意,查询是在我的代码中动态构建的。另请注意,当先前最有利的行被标记为不可用时,第二个用户将自动转到下一个,依此类推。此外,处理不同类别的不同用户不必等待彼此的锁被释放。最糟糕的是,5秒后,将返回错误,操作将被取消。
最后,问题是:如何在SQL Server中实现相同的结果?我一直在寻找锁定提示,理论上看起来它们应该起作用。但是,防止其他锁定的唯一锁定是“UPDLOCK”和“XLOCK”,它们只能在工作台级别工作。
那些在行级别工作的锁定提示都是共享锁,它们也不能满足我的需求(两个用户可以同时锁定同一行,都将其标记为不可用并对相应的项执行冗余操作)。
有些人似乎添加了“时间修改”列,以便会话可以验证他们是修改它的人,但这听起来会有很多冗余和不必要的访问。
答案 0 :(得分:14)
你可能正在寻找with (updlock, holdlock)
。这将使select
获取exclusive
锁定,这是更新所需的锁定,而不是shared
锁定。 holdlock
提示告诉SQL Server保持锁定,直到事务结束。
FROM TABLE_ITEM with (updlock, holdlock)
答案 1 :(得分:7)
在SQL Server中有锁定提示,但它们不会像您提供的Oracle示例那样跨越它们的语句。在SQL Server中执行此操作的方法是在包含要执行的语句的事务上设置隔离级别。请参阅this MSDN page,但一般结构如下所示:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
select * from ...
update ...
COMMIT TRANSACTION;
SERIALIZABLE是最高的隔离级别。请参阅其他选项的链接。来自MSDN:
SERIALIZABLE指定以下内容:
语句无法读取已修改但尚未修改的数据 由其他交易承诺。
没有其他事务可以修改已被读取的数据 当前事务完成之前的当前事务。
其他事务无法插入具有键值的新行 落在当前任何语句读取的键范围内 交易直到当前交易完成。
答案 2 :(得分:4)
正如documentation所说:
XLOCK
指定要占用独占锁,直到 交易完成。如果使用ROWLOCK,PAGLOCK或TABLOCK指定, 独占锁适用于适当的粒度级别。
所以解决方案是使用 <?php
// the query
$wpb_all_query = new WP_Query(array('post_type'=>'post',
'post_status'=>'publish',
'posts_per_page'=> 10, // As per the post below you need to have a set number of posts.
'offset' => 1
)); ?>
<?php if ( $wpb_all_query->have_posts() ) : ?>
:
WITH(XLOCK, ROWLOCK)
答案 3 :(得分:1)
您是否尝试过 WITH(ROWLOCK)?
BEGIN TRAN
UPDATE your_table WITH (ROWLOCK)
SET your_field = a_value
WHERE <a predicate>
COMMIT TRAN