我有一列,只有当我传递给存储过程的值与列中的值不同时,我才想更新它。如果这很重要的话,它恰好是NVARCHAR(255)列。
每次写这个值的利弊是什么?如果我传入的内容与数据库中的内容不同,那么先检查值并只进行写入的利弊是什么?
我在写作前进行比较的简化示例:
-- @URL and @ContentName are parameters of the sproc
SET @ExistingURL =
(SELECT TOP 1 C.URL
FROM Content C
WHERE ContentName = @ContentName)
-- update only if the parameter and existing value are different
IF(@ExistingURL != @URL)
BEGIN
UPDATE Content
SET URL = @URL
WHERE ContentName = @ContentName
END
您的查询似乎可以按如下方式重写,而无需将任何内容放入局部变量中。
UPDATE dbo.Content
SET URL = @URL
WHERE ContentName = @ContentName
AND URL <> @URL;
假设ContentName
是唯一的,这将影响0或1行。没有必要通过将URL的检查拉到一个单独的查询中来使逻辑更加复杂。"只在需要时写入"部分由WHERE
子句负责。
现在,如果你要将其扩展到多个列来更新,那么你想要的并不是那么简单(我认为这不值得)。例如,假设有另一个列名title
,并且有一个名为@title
的变量。您预测,当URL
发生变化时只更新它,而当title
发生变化时仅更新它将是明智的。理论上,你可以通过几种方式来实现这一点(请记住,这一切都假设变量和列都不可为null——如果是,这并不会改变这些方法,只会让语法变得更令人生畏):
运行两个独立的更新
UPDATE dbo.Content SET URL = @URL WHERE ... -- actual where clause AND URL <> @URL; UPDATE dbo.Content SET title = @title WHERE ... -- same where clause AND title <> @title;
有条件地更新
UPDATE dbo.Content SET URL = CASE WHEN URL <> @URL THEN @URL ELSE URL END, title = CASE WHEN title <> @title THEN @title ELSE title END WHERE ... -- same where clause AND (URL <> @URL OR title <> @title);
我怀疑后者的表现不会比更自然的写这篇文章的方式更好,因为你无论如何都在锁定并写入行(但除了上面评论中Mike的支持之外,我没有任何可靠的证据):
UPDATE dbo.Content
SET URL = @URL, title = @title
WHERE ... -- same where clause
AND (URL <> @URL OR title <> @title);
换句话说,只需更新所有值,而不管实际更改了哪些值。
有一个简单的模式可以解决这个问题:
update my_table set
my_col = 'newValue'
where id = 'someKey'
and my_col != 'newValue';
此查询具有索引读取的性能,重要的是,如果值不需要更新,则不锁定任何行。
只有当值需要更新时,where子句才会命中一行并锁定它(尽管只是短暂的)以进行更新。
您可以使用相同的逻辑将其扩展为处理多个列:
update my_table set
my_col1 = 'newValue1',
my_col2 = 'newValue2'
where id = 'someKey'
and (my_col1 != 'newValue1'
or my_col2 != 'newValue2');
名义上,写入所需的时间是读取的两倍
写操作将放置一个锁,这样它就可能阻止读取
它将在事务日志中放置一个条目
如果您有该值的触发器,该怎么办。
作为练习,我测试<>在任何活动数据库上。
若表处于活动状态,锁定每一行可能是一大成功
您的更新必须获取锁
其他查询必须等待您的锁(除非它们接受脏读取)。
如果您实际上只更新大活动表上10%的行,并且不测试<>而盲目更新所有行那么这就是一个大热门。
如果你正在更新99%的行,那么你会受到一个小的打击
测试<>需要时间。
通过检查,你可能会受到小打击,但如果不检查,你(和其他人)可能会受到大打击。
检查<>的示例更新
Update [docSVsys] set [ftsStatus] = 5 where [ftsStaus] <> 5
锁在整行上,而不是单个列上。如果更新的值超过1,那么仍然重要的是%的行。
Update [docSVsys] set [ftsStatus] = 5, [wfID] = 3
where [ftsStatus] <> 5 or [wfID] <> 3
如果只有一个值发生了更改,则更新这两个值都不会成功,因为它必须获取行上的更新锁定。
许多小时后发布的实际声明。
正如亚伦所说的那样,左派的加入毫无作用。
目前,第一个查询返回@URL或null
而且它不是基于ContentName。
所以看看
if(@ExistingURL != @URL)
只有在表中没有一行具有该值的情况下,情况才会如此。
唯一约束与只允许一个null的约束不同。
我只是很惊讶你的问题不是。此更新失败?