我想根据一个简单的标准更新一组行,并获取已更改的PK列表。我以为我可以做这样的事情,但我担心可能的并发问题:
SELECT Id FROM Table1 WHERE AlertDate IS NULL;
UPDATE Table1 SET AlertDate = getutcdate() WHERE AlertDate IS NULL;
如果包含在事务中,是否存在可能发生的并发问题?或者有更好的方法吗?
答案 0 :(得分:69)
考虑查看OUTPUT clause:
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
答案 1 :(得分:13)
处理此问题的一种方法是在事务中执行此操作,并使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
答案 2 :(得分:10)
许多年后......
使用OUTPUT子句的公认答案是好的。我不得不挖掘实际的语法,所以这里是:
DECLARE @UpdatedIDs table (ID int)
UPDATE
Table1
SET
AlertDate = getutcdate()
OUTPUT
inserted.Id
INTO
@UpdatedIDs
WHERE
AlertDate IS NULL;
ADDED 2015年9月14日:
"我可以使用标量变量而不是表变量吗?"有人可能会问...抱歉,但不,你不能。如果您需要一个ID,则必须SELECT @SomeID = ID from @UpdatedIDs
。
答案 3 :(得分:8)
首先执行UPDATE会更容易,然后运行'SELECT ID FROM INSERTED'。
请查看SQL Tips了解更多信息和示例。
答案 4 :(得分:4)
也许更像这样的事情?
declare @UpdateTime datetime
set @UpdateTime = getutcdate()
update Table1 set AlertDate = @UpdateTime where AlertDate is null
select ID from Table1 where AlertDate = @UpdateTime
答案 5 :(得分:1)
我遇到了同样的问题;我必须更新信用额度,并且必须修改时间以及来自数据库的信用额度。基本上是
在MYSQL中同步/原子执行(更新然后获取)
我尝试了很多选项,找到了一个解决了我的问题。
1)OPTION_1选择更新
这是维持锁定直到更新(从GET到UPDATE的SYNC),但我需要在更新后锁定直到GET。
2)OPTION_2存储过程
存储过程不会像redis lua那样同步执行,所以我们还需要同步代码才能执行。
3)OPTION_3交易
我在下面使用了JPA entityManager,认为在提交之前没有人可以更新,并且在提交之前我将获得更新的对象以及修改的时间(来自DB)。但我没有得到最新的对象。只提交我得到最新的。
client.DefaultRequestHeaders.Remove("X-RequestHeader");
client.DefaultRequestHeaders.Add("X-RequestHeader", $"request ServiceId=\"{serviceId}\"");
4)OPTION_4 LOCK解决了这个问题,所以在更新之前我获得了锁;然后在GET之后我释放了锁。
try {
entityManager.getTransaction().begin();
//entityManager.persist(object);
int upsert = entityManager.createNativeQuery(
"update com.bill.Credit c set c.balance = c.balance - ?1
where c.accountId = ?2 and c.balance >= ?1").executeUpdate();
//c.balance >= ? for limit check
Credit newCredit = entityManager.find(Credit.class, "id");
entityManager.refresh(newCredit); //SHOULD GET LATEST BUT NOT
entityManager.getTransaction().commit();
} finally {
entityManager.unwrap(Session.class).close();
}
答案 6 :(得分:0)
如果它在事务内部,数据库锁定系统将处理并发问题。当然,如果你使用一个(mssql默认是它使用锁,所以如果你不重写那么它就说明了)
答案 7 :(得分:0)
编辑:我的不好,您希望select在更新后显示结果,而不是从select更新。
您是否尝试过子选择?
update mytable set mydate = sysdate
where mydate in (select mydate from mytable where mydate is null);
答案 8 :(得分:0)
在SQL 2008中引入了新的TSQL语句“MERGE”,它根据与源表的连接结果对目标表执行插入,更新或删除操作。您可以根据另一个表中的差异在一个表中插入,更新或删除行来同步两个表。
http://blogs.msdn.com/ajaiman/archive/2008/06/25/tsql-merge-statement-sql-2008.aspx http://msdn.microsoft.com/en-us/library/bb510625.aspx