在更新中将列的先前值另存为变量是否安全?

时间:2019-01-09 08:28:31

标签: sql sql-server tsql

想像这样一个简单的更新存储过程:

CREATE PROCEDURE [UpdateMyTable] (
    @Id int,
    @ModifiedOn datetime,
    @GeneratedOn datetime
)
AS
UPDATE
    [MyTable]
SET
    [ModifiedOn] = @ModifiedOn
WHERE
    [Id] = @Id AND [ModifiedOn] <= @GeneratedOn

现在,要基于ModifiedOn的先前值返回结果,我将其更改为:

ALTER PROCEDURE [UpdateMyTable] (
    @Id int,
    @ModifiedOn datetime,
    @GeneratedOn datetime
)
AS
DECLARE @PreviousModifiedOn datetime
UPDATE
    [MyTable]
SET
    [ModifiedOn] = @ModifiedOn,
    @PreviousModifiedOn = [ModifiedOn]
WHERE
    [Id] = @Id AND [ModifiedOn] <= @GeneratedOn

IF @PreviousModifiedOn <= @GeneratedOn
    SELECT @ModifiedOn
ELSE
    SELECT -1

在SET部分中将@PreviousModifiedOn变量填充为ModifiedOn的先前值是否安全?还是在将ModifiedOn值保存到变量之前更改它?


更新

使用OUTPUT进行相同的查询:

ALTER PROCEDURE [UpdateMyTable] (
    @Id int,
    @ModifiedOn datetime,
    @GeneratedOn datetime
)
AS
DECLARE @PreviousModifiedOn AS TABLE (ModifiedOn datetime)
UPDATE
    [MyTable]
SET
    [ModifiedOn] = @ModifiedOn
OUTPUT
    Deleted.[ModifiedOn] INTO @PreviousModifiedOn
WHERE
    [Id] = @Id AND [ModifiedOn] <= @GeneratedOn

IF EXISTS (SELECT * FROM @PreviousModifiedOn WHERE [ModifiedOn] <= @GeneratedOn)
    SELECT @ModifiedOn
ELSE
    SELECT -1

看来OUTPUT是解决问题的正确方法,但是由于变量表,我认为它的性能成本更高。

所以我的问题是...为什么使用OUTPUT比我的解决方案好?我的解决方案有什么问题吗?在性能和速度上哪个更好?

2 个答案:

答案 0 :(得分:3)

我相信这是安全的。尽管变量赋值是专有扩展,但SET子句的其余部分遵循此处的SQL标准-所有赋值均按 进行计算,就像它们并行发生一样。也就是说,赋值右边的所有表达式都是基于所有列的更新前值来计算的。

例如为什么UPDATE Table SET A=B, B=A会交换两列的内容,而不是将它们都设置为等于先前的B

对我来说,要警惕的一件事是UPDATE可能执行了 no 分配(由于WHERE子句),因此仍然是NULL,或者可能已经执行了多个分配;在后一种情况下,您的变量将设置为先前的值之一,但不能保证它将保留哪个 row 值。

答案 1 :(得分:3)

这不是必需的,因为MS SQL Server 2005可以在这种情况下使用OUTPUT。

    If Button1.CheckState = CheckState.Checked Then
        Button1.Image = My.Resources.icon1
    Else
        Button1.Image = My.Resources.icon2
    End If