如果不同/更改则更新

nam*_*ami 32 sql t-sql sql-server sql-server-2005

是否可以在sql中执行更新语句,但只有在更新不同时才更新?

例如

如果在数据库中, col1 = "hello"

update table1 set col1 = 'hello'
Run Code Online (Sandbox Code Playgroud)

不应该执行任何类型的更新

但是,如果

update table1 set col1 = "bye"
Run Code Online (Sandbox Code Playgroud)

应该执行更新.

sll*_*sll 28

如果新值与DB中的值相同,则不要执行任何更新

WHERE col1 != @newValue
Run Code Online (Sandbox Code Playgroud)

(显然也应该有一些Id字段来识别一行)

WHERE Id = @Id AND col1 != @newValue
Run Code Online (Sandbox Code Playgroud)

PS:最初你只想在值为'bye'的情况下进行更新,所以只需添加AND col1 = 'bye',但我觉得这是多余的,我只是想

  • 无论你的Sql语言如何定义equals运算符,想法都很重要 (8认同)
  • 可能值得注意的是,如果col1为NULL,则不会更新该值 - 使其成为`WHERE Id = @Id AND(col1!= @ newValue OR col1 IS NULL)`如果col1为null是可能的 (7认同)

Dud*_*001 24

在查询编译和执行期间,SQL Server不会花时间确定UPDATE语句是否实际更改任何值.它只是按预期执行写操作,即使没有必要.

在场景中

update table1 set col1 = 'hello'
Run Code Online (Sandbox Code Playgroud)

你可能会认为SQL不会做任何事情,但它会 - 它将执行所有必要的写入,就好像你实际上已经改变了值一样.对于物理表(或聚簇索引)以及在该列上定义的任何非聚集索引,都会发生这种情况.这会导致写入物理表/索引,重新计算索引和事务日志写入.使用大型数据集时,仅更新将接收更改的行具有巨大的性能优势.

如果我们想在不必要时避免这些写入的开销,我们必须设计一种方法来检查是否需要更新.检查更新需求的一种方法是添加类似"where col <>'hello'的内容.

update table1 set col1 = 'hello' where col1 <> 'hello'
Run Code Online (Sandbox Code Playgroud)

但是在某些情况下这不会很好,例如,如果要更新具有许多行的表中的多个列,并且这些行中只有一小部分实际上会更改其值.这是因为需要对所有这些列进行过滤,并且非等式谓词通常不能使用索引查找,以及如上所述的表和索引写入和事务日志条目的开销.

但是使用EXISTS子句和EXCEPT子句的组合有一个更好的选择.我们的想法是将目标行中的值与匹配源行中的值进行比较,以确定是否确实需要更新.查看下面的修改后的查询,并检查以EXISTS开头的附加查询过滤器.注意SELECT语句在EXISTS子句中没有FROM子句.这一部分特别重要,因为这只会在查询计划中增加额外的常量扫描和过滤操作(两者的成本都是微不足道的).所以你最终得到的是一个非常轻量级的方法,用于确定首先是否需要UPDATE,从而避免不必要的写入开销.

update table1 set col1 = 'hello'
/* AVOID NET ZERO CHANGES */
where exists 
    (
    /* DESTINATION */
    select table1.col1
    except
    /* SOURCE */
    select col1 = 'hello'
    )
Run Code Online (Sandbox Code Playgroud)

当您为具有文字值的表中的所有行更新一个值时,对于原始问题中的简单场景检查简单WHERE子句中的更新,这看起来过于复杂.但是,如果要更新表中的多个列,并且更新源是另一个查询并且您希望最小化写入和事务日志条目,则此方法非常有效.它也比使用<>测试每个字段更好.

一个更完整的例子可能是

update table1
   set col1 = 'hello',
       col2 = 'hello',
       col3 = 'hello'
/* Only update rows from CustomerId 100, 101, 102 & 103 */
where table1.CustomerId IN (100, 101, 102, 103)
/* AVOID NET ZERO CHANGES */
  and exists 
    (
    /* DESTINATION */
    select table1.col1
           table1.col2
           table1.col3
    except
    /* SOURCE */
    select z.col1,
           z.col2,
           z.col3
      from #anytemptableorsubquery z
     where z.CustomerId = table1.CustomerId
    )
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你的解释:)我来到这里因为我确实有你描述的不必要的写问题.似乎Amazon RDS上的PostgreSQL也会盲目地写行,即使它们没有变化.因此,在一个大表上,发出一个`UPDATE用户SET状态= 1;`可能会耗尽你的IOPS(从而导致之后的高延迟),同时`UP​​DATE用户SET状态= 1 WHERE状态<> 1;`可以满足内存缓存,因此不会浪费任何宝贵的IOPS. (2认同)
  • 最后一个示例也适用于存储过程,因为临时表可以替换为参数,例如“select @param1、@param2 等”。 (2认同)

ype*_*eᵀᴹ 10

如果要'hello'仅将字段更改为'bye',请使用以下命令:

UPDATE table1
SET col1 = 'hello'
WHERE col1 = 'bye'
Run Code Online (Sandbox Code Playgroud)

如果您只想更新它'hello',请使用:

UPDATE table1
SET col1 = 'hello'
WHERE col1 <> 'hello'
Run Code Online (Sandbox Code Playgroud)

有这种奇怪方法的原因吗?正如丹尼尔评论的那样,没有特别的收获 - 除非你有数千行col1='hello'.是这样的吗?


Dan*_*rth 6

这可以通过更新前触发器实现.在此触发器中,您可以将旧值与新值进行比较,如果没有差异则取消更新.但这会导致来电者网站出错.
我不知道,你为什么要这样做,但这里有几种可能性:

  1. 性能:这里没有性能提升,因为更新不仅需要找到正确的行,还需要比较数据.
  2. 触发:如果您希望仅在发生实际更改时触发触发器,则需要像这样实现触发器,以便在执行任何操作之前将所有旧值与新值进行比较.

  • 在IOPS有限的Amazon RDS上,可以获得极强的性能优势.检查行是否被修改可能是从内存缓存中完成的,因此不使用IOPS.另一方面,不必要地写行将耗尽IOPS.一旦IOPS耗尽,您的查询将花费几分钟写在一张大桌子上.我已经看到这导致了大型AWS RDS实例上0.1ms和640秒之间的差异. (5认同)
  • 当更新中没有值更改但您想避免更改“ModifiedDate”值时,它会很有帮助。但我认为使用存储过程可以轻松完成,因为仅通过存储过程完成所有 DML 操作并不是一个坏主意。 (2认同)