目标是防止更新新值与旧值相同的行(因为表具有我不想重置的自动更新时间戳(。
问题是某些列可为空,因此"新建"<>"旧"不起作用,因为如果"新建"或"旧"为 NULL,则会导致 NULL。
解决方法是使用 ISNULL,但如果替换值可能是表中的有效值,则这不起作用:
UPDATE tableName
SET ColumnName = @newValue
WHERE ISNULL(ColumnName, 'ValueIsNull') <> ISNULL(@newValue, 'ValueIsNull')
我发现的另一种方法是比较值,然后检查 XOR 新旧以查看其中之一是否为 NULL。这不是很清楚,特别是如果您必须检查多列:
UPDATE tableName
SET ColumnName = @newValue
WHERE
ColumnName <> @newValue
OR (
(ColumnName IS NULL AND @newValue IS NOT NULL)
OR (ColumnName IS NOT NULL AND @newValue IS NULL)
)
有没有更好的方法可以做到这一点?
我喜欢这种方法,因为您可以轻松地同时比较多列。运行它,您将看到它仅在您关心的列发生变化时才更新行。
DECLARE @T1 TABLE (ID INT, Val1 VARCHAR(20),Val2 VARCHAR(20),LastUpdated DATETIME,VersionNum INT)
DECLARE @Val1 VARCHAR(20)='test'
DECLARE @Val2 VARCHAR(20)='test2'
INSERT INTO @T1 (ID,Val1,Val2,LastUpdated,VersionNum) SELECT 1,@Val1,@Val2,GETDATE(),1
SELECT * FROM @T1
UPDATE @T1
SET Val1=@Val1,Val2=@Val2, LastUpdated=GETDATE(),VersionNum=VersionNum+1
WHERE BINARY_CHECKSUM(Val1,Val2)<>BINARY_CHECKSUM(@Val1,@Val2)
SELECT * FROM @T1
SET @Val1 ='test'
SET @Val2 ='Oooooo'
UPDATE @T1
SET Val1=@Val1,Val2=@Val2, LastUpdated=GETDATE(),VersionNum=VersionNum+1
WHERE BINARY_CHECKSUM(Val1,Val2)<>BINARY_CHECKSUM(@Val1,@Val2)
SELECT * FROM @T1
这里有一个技巧:
UPDATE t
SET ColumnName = @newValue
FROM tableName t CROSS APPLY
(SELECT COUNT(*) as num_values
FROM (SELECT DISTINCT val
FROM (VALUES (ColumnName), (@newValue) ) v(val)
) v
) v
WHERE num_values <> 1;
SQL Server 不支持IS DISTINCT FROM
。 但是,聚合具有您想要的正确逻辑。 如果值相等或两者NULL
,则计数为 1。 否则,计数将为 0 或 2。