2

我有一列,只有当我传递给存储过程的值与列中的值不同时,我才想更新该列。如果重要的话,它恰好是一个 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
4

3 回答 3

2

看起来您的查询可以按如下方式重写,而无需将任何内容放入局部变量中。

UPDATE dbo.Content
  SET URL = @URL
  WHERE ContentName = @ContentName
  AND URL <> @URL;

假设它ContentName是唯一的,这将影响 0 或 1 行。无需通过将 URL 的检查拉到单独的查询中来使逻辑更复杂。“仅在需要时写入”部分由该WHERE子句处理。

现在,如果您要将其扩展到多个列进行更新,那么您所追求的并不是那么简单(而且我认为这不值得)。例如,假设有另一个列 name title,并且您有一个名为的变量@title。您预测仅URL在更改时才更新,并且仅在更改时才更新将是明智的title。理论上,您可以通过多种方式完成此操作(请记住,这一切都假设变量和列都不可为空 - 如果它们是,它不会改变这些方法,只是使语法有点更令人生畏):

  1. 运行两个独立的更新

    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;
    
  2. 有条件地更新

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

我怀疑后者不会比写这个更自然的方式表现更好,因为无论如何你都在锁定并写入行(但除了迈克的认可之外,我没有任何可靠的证据上面的评论):

UPDATE dbo.Content
  SET URL = @URL, title = @title
WHERE ... -- same where clause
AND (URL <> @URL OR title <> @title);

换句话说,只需更新所有值,而不管哪些值实际发生了变化。

于 2012-09-25T23:59:15.643 回答
1

名义上,写入的时间是读取的两倍。
写入将放置一个锁,因此它可能会阻止读取。
它将在事务日志中放置一个条目。
如果您对该值有触发器怎么办。

作为实践,我在任何活动数据库上测试 <>。

如果表处于活动状态,则对每一行进行锁定可能会受到很大影响。
您的更新必须获取锁。
其他查询必须等待您的锁(除非它们接受脏读)。

如果您实际上只更新大型活动表上 10% 的行,并且通过不测试 <> 来盲目更新所有行,那么这是一个很大的打击。

如果您要更新 99% 的行,那么您会受到一点打击。
测试 <> 需要时间。

过牌你可能会受到小打击,但不过牌你(和其他人)可能会受到很大打击。

检查 <> 的示例更新

Update [docSVsys] set [ftsStatus] = 5 where [ftsStaus] <> 5 

锁在整行上 - 而不是单个列。如果您要更新超过 1 个值,那么仍然重要的是行的百分比。

Update [docSVsys] set [ftsStatus] = 5, [wfID] = 3 
where [ftsStatus] <> 5 or [wfID] <> 3

如果只有一个值更改,则更新两者都不是命中,因为它必须获取行上的更新锁。

几个小时后发布的实际声明。

正如 Aaron 所说,左连接什么都不做。

就目前而言,第一个查询返回 @URL 或 null。
而且它不是基于 ContentName。

所以看着

if(@ExistingURL != @URL)

仅当表中没有行具有该值时,这才是正确的。

唯一约束与它只允许一个空值不同。

我很惊讶你的问题不是。这次更新失败?

于 2012-09-26T00:33:50.800 回答
1

有一个简单的模式可以解决这个问题:

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');
于 2012-09-26T00:59:38.270 回答