每次与查询重写值以查看值是否已更改

时间:2012-09-25 22:47:28

标签: sql sql-server performance tsql

只有当我传入存储过程的值与列中的值不同时,我才会更新列。它恰好是一个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

3 个答案:

答案 0 :(得分:2)

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

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

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

现在,如果您要将此扩展到多个列进行更新,那么您所追求的并不是那么简单(我认为这不值得)。例如,假设有另一个列名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);
    
  3. 我怀疑后者不会比更自然的方式更好地执行此操作,因为无论如何你都要锁定并写入行(但除了代言之外,我没有任何可靠的证据证明来自迈克的上述评论):

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

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

答案 1 :(得分:1)

名义上写入的时间是读取的两倍 写入将锁定,因此它可能会阻止读取 它会在交易日志中输入一个条目 如果您触发该值,该怎么办?

作为一种惯例,我测试&lt;&gt;在任何活动数据库上。

如果桌子处于活动状态,对每一行进行锁定可能会受到重创 您的更新必须获取锁 其他查询必须等待你的锁(除非他们接受脏读)。

如果你只是实际更新了一个大型活动表上的10%的行,并且通过不测试&lt;&gt;而盲目更新所有行那是一个很大的打击。

如果你要更新99%的行,那么你会受到轻微的打击 测试&lt;&gt;需要时间。

通过检查你可能会受到轻微打击,但不检查你(和其他人)可能会受到重创。

检查&lt;&gt;

的更新示例
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相同。

我很惊讶你的问题不是。此更新失败了吗?

答案 2 :(得分: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');