有没有办法同时select和更新行?

我想根据一个简单的标准更新一组行,并获得已更改PK的列表。 我以为我可以做这样的事情,但担心可能的并发问题:

SELECT Id FROM Table1 WHERE AlertDate IS NULL; UPDATE Table1 SET AlertDate = getutcdate() WHERE AlertDate IS NULL; 

如果这是包装在一个事务中是否有可能发生的并发问题? 还是有更好的方法来做到这一点?

考虑查看UPDATEOUTPUT子句function (也是DELETEINSERT )。 链接的MSDN页面中的示例:

 UPDATE TOP (10) HumanResources.Employee SET VacationHours = VacationHours * 1.25, ModifiedDate = GETDATE() OUTPUT inserted.BusinessEntityID, deleted.VacationHours, inserted.VacationHours, inserted.ModifiedDate INTO @MyTableVar; 

处理这种情况的一种方法是在事务中执行此操作,并使SELECT查询对所选的行执行更新locking,直到事务完成。

 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之间select的行的可能性。

当你提交事务时,更新锁将被释放。

处理这个问题的另一种方法是用FOR UPDATE选项为你的SELECT声明一个游标。 然后更新当前的游标。 以下是未经testing,但应给你的基本思路:

 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 

先做更新会比较容易,然后运行“SELECT ID FROM INSERTED”。

看看SQL提示了解更多信息和示例。

多年以后…

使用OUTPUT子句的接受的答案是好的。 我不得不挖掘实际的语法,所以这里是:

 DECLARE @UpdatedIDs table (ID int) UPDATE Table1 SET AlertDate = getutcdate() OUTPUT inserted.Id INTO @UpdatedIDs WHERE AlertDate IS NULL; 

2015年9月14日新增

“我可以使用标量variables而不是表variables吗?” 有人可能会问…对不起,但是不行。 如果您需要一个ID,您必须SELECT @SomeID = ID from @UpdatedIDs ID。

也许更像这样的东西?

 declare @UpdateTime datetime set @UpdateTime = getutcdate() update Table1 set AlertDate = @UpdateTime where AlertDate is null select ID from Table1 where AlertDate = @UpdateTime 

如果它在事务中,数据库locking系统将处理并发问题。 当然,如果你使用一个(默认的mssql是它使用了locking,所以它表明如果你不覆盖)

在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

编辑:我不好,你想select显示更新后的结果,而不是从select更新。

你尝试过一个子select?

 update mytable set mydate = sysdate where mydate in (select mydate from mytable where mydate is null); 

我面临同样的问题; 我必须更新信用额度,并且必须获得修改时间以及来自DB的信用详情。 基本上是这样

同步/primefaces执行(更新然后GET)在MYSQL

我尝试了很多select,发现一个解决了我的问题。

1)OPTION_1 SELECT FOR UPDATE

这是维持锁直到更新(SYNC从GET到UPDATE),但我需要locking后更新,直到GET。

2)OPTION_2存储过程

存储过程不会像redis lua那样同步执行,所以我们还需要同步代码来执行。

3)OPTION_3交易

我已经使用JPA entityManager像下面,认为提交之前没有人可以更新,并在提交之前,我会得到更新的对象与修改时间(从数据库)。 但是我没有得到最新的对象。 只有提交我得到了最新的。

  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(); } 

4)OPTION_4 LOCK解决了这个问题,所以在更新之前,我获得了锁; 那么GET之后,我已经释放了锁。

 private Object getLock(final EntityManager entityManager, final String Id){ entityManager.getTransaction().begin(); Object obj_acquire = entityManager.createNativeQuery("SELECT GET_LOCK('" + Id + "', 10)").getSingleResult(); entityManager.getTransaction().commit(); return obj_acquire; } private Object releaseLock(final EntityManager entityManager, final String Id){ entityManager.getTransaction().begin(); Object obj_release = entityManager.createNativeQuery("SELECT RELEASE_LOCK('" + Id + "')").getSingleResult(); entityManager.getTransaction().commit(); return obj_release; }