我想基于一个简单的标准更新一组行,并获取已更改的PK列表.我以为我可以做这样的事情,但我担心可能的并发问题:
SELECT Id FROM Table1 WHERE AlertDate IS NULL; UPDATE Table1 SET AlertDate = getutcdate() WHERE AlertDate IS NULL;
如果包含在事务中,是否存在可能发生的并发问题?或者有更好的方法吗?
考虑查看OUTPUT子句:
USE AdventureWorks2012; GO DECLARE @MyTableVar table( EmpID int NOT NULL, OldVacationHours int, NewVacationHours int, ModifiedDate datetime); UPDATE TOP (10) HumanResources.Employee SET VacationHours = VacationHours * 1.25, ModifiedDate = GETDATE() OUTPUT inserted.BusinessEntityID, deleted.VacationHours, inserted.VacationHours, inserted.ModifiedDate INTO @MyTableVar; --Display the result set of the table variable. SELECT EmpID, OldVacationHours, NewVacationHours, ModifiedDate FROM @MyTableVar; GO --Display the result set of the table. SELECT TOP (10) BusinessEntityID, VacationHours, ModifiedDate FROM HumanResources.Employee; GO
处理此问题的一种方法是在事务中执行此操作,并使SELECT查询对所选行执行更新锁定,直到事务完成.
BEGIN TRAN SELECT Id FROM Table1 WITH (UPDLOCK) WHERE AlertDate IS NULL; UPDATE Table1 SET AlertDate = getutcdate() WHERE AlertDate IS NULL; COMMIT TRAN
这消除了并发客户端更新SELECT和UPDATE之间选择的行的可能性.
提交事务时,将释放更新锁.
另一种处理方法是使用FOR UPDATE选项为SELECT声明一个游标.然后更新当前CURSOR的当前.以下未经过测试,但应该为您提供基本想法:
DECLARE cur1 CURSOR FOR SELECT AlertDate FROM Table1 WHERE AlertDate IS NULL FOR UPDATE; DECLARE @UpdateTime DATETIME SET @UpdateTime = GETUTCDATE() OPEN cur1; FETCH NEXT FROM cur1; WHILE @@FETCH_STATUS = 0 BEGIN UPDATE Table1 AlertDate = @UpdateTime WHERE CURRENT OF cur1; FETCH NEXT FROM cur1; END
许多年后......
使用OUTPUT子句的公认答案是好的.我不得不挖掘实际的语法,所以这里是:
DECLARE @UpdatedIDs table (ID int) UPDATE Table1 SET AlertDate = getutcdate() OUTPUT inserted.Id INTO @UpdatedIDs WHERE AlertDate IS NULL;
2015年9月14日新增:
"我可以使用标量变量而不是表变量吗?" 有人可能会问...抱歉,但不,你不能.SELECT @SomeID = ID from @UpdatedIDs
如果您需要一个ID ,则必须这样做.
首先执行UPDATE然后运行'SELECT ID FROM INSERTED'会更容易.
有关更多信息和示例,请查看SQL技巧.
也许更像这样?
declare @UpdateTime datetime set @UpdateTime = getutcdate() update Table1 set AlertDate = @UpdateTime where AlertDate is null select ID from Table1 where AlertDate = @UpdateTime