MSSQL更新:更新前的输出值

时间:2017-08-17 13:29:02

标签: sql sql-server tsql

有一个包含IDU(PK)和stat列的表格。如果stat的第一位为1,我需要将其设置为0并仅在这种情况下运行一些存储过程,否则我什么都不做。

以下是此

的简单查询
DECLARE @s INT
-- get the current value of status before update
SET @s = (SELECT stat FROM  myTable
          WHERE IDU = 999999999)
-- check it first bit is 1
IF (@s & 1) = 1  
BEGIN
    -- first bit is 1, set it to 0
    UPDATE  myTable
    SET status = Stat & ~1
    WHERE IDU = 999999999
    -- first bit is one, in this case we run our SP
    EXEC SOME_STORED_PROCEDURE
END 

但我不确定这个查询是否是最佳的。我听说过OUTPUT查询的UPDATE参数,但我发现了如何获取插值。有没有办法获得插入之前的值?

3 个答案:

答案 0 :(得分:2)

是的,OUTPUT子句允许您在更新之前获取之前的值。您需要查看deletedinserted表。

  

DELETED

     

是一个列前缀,用于指定由中删除的值   更新或删除操作。以DELETED为前缀的列反映了   完成UPDATE,DELETE或MERGE语句之前的值。

  

INSERTED

     

是一个列前缀,用于指定插入或添加的值   更新操作。以INSERTED为前缀的列反映了该值   在UPDATE,INSERT或MERGE语句完成之后但之前   触发器被执行。

-- Clear the first bit without checking what it was

DECLARE @Results TABLE (OldStat int, NewStat int);

UPDATE myTable
SET Stat = Stat & ~1
WHERE IDU = 999999999
OUTPUT
    deleted.Stat AS OldStat
    ,inserted.Stat AS NewStat
INTO @Results
;

-- Copy data from @Results table into variables for comparison
-- Assumes that IDU is a primary key and @Results can have only one row

DECLARE @OldStat int;
DECLARE @NewStat int;

SELECT @OldStat = OldStat, @NewStat = NewStat
FROM @Results;

IF @OldStat <> @NewStat 
BEGIN
    EXEC SOME_STORED_PROCEDURE;
END;

答案 1 :(得分:1)

无论最佳情况如何,此查询都不是100%安全的。这是因为在SET @s = ...和UPDATE myTable之间,无法保证stat的值没有被更改。如果此代码多次运行,如果两个案例对同一个IDU执行致命关闭,则可能会搞砸。第一个线程会正常,但第二个线程不会,因为第一个线程会在第二个线程读取之后和更新之前更改统计信息。即使在SERIALIZABLE隔离上,select语句也不会锁定超出自己的执行时间。

为了安全起见,你需要在读取之前锁定记录,为此你需要一个更新声明,甚至假的:

DECLARE @s INT
BEGIN TRANSACTION
UPDATE myTable SET stat = stat WHERE IDU = 999999999 --now you row lock your row, make sure no other thread can move along
-- get the current value of status before update
SET @s = (SELECT stat FROM  myTable
          WHERE IDU = 999999999)
-- check it first bit is 1
IF (@s & 1) = 1  
BEGIN
    -- first bit is 1, set it to 0
    UPDATE  myTable
    SET status = Stat & ~1
    WHERE IDU = 999999999
    -- first bit is one, in this case we run our SP
-- COMMIT TRANSACTION here? depends on what SOME_STORED_PROCEDURE does
    EXEC SOME_STORED_PROCEDURE
COMMIT TRANSACTION --i believe here you release the row lock

我不确定你的意思是“有没有办法获得插入前的值”,因为你只更新了唯一的数据,stat,你已经从旧记录中读取过,无论你是否更新

答案 2 :(得分:0)

您可以使用INSTEAD OF UPDATE触发器执行此操作。