如果不同/更改则更新

时间:2011-07-13 10:30:32

标签: sql sql-server sql-server-2005 tsql

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

例如

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

update table1 set col1 = 'hello'

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

然而,如果

update table1 set col1 = "bye"

应该执行更新。

10 个答案:

答案 0 :(得分:25)

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

WHERE col1 != @newValue

(显然还应该有一些Id字段来标识一行)

WHERE Id = @Id AND col1 != @newValue

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

答案 1 :(得分:17)

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

在类似

的场景中
update table1 set col1 = 'hello'

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

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

update table1 set col1 = 'hello' where col1 <> 'hello'

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

但是使用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'
    )

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

测试每个字段更好

更完整的示例可能是

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
    )

答案 2 :(得分:8)

如果您想将字段更改为'hello' 'bye',请使用此字段:

UPDATE table1
SET col1 = 'hello'
WHERE col1 = 'bye'

如果只想与'hello'不同时更新,请使用:

UPDATE table1
SET col1 = 'hello'
WHERE col1 <> 'hello'

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

答案 3 :(得分:6)

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

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

答案 4 :(得分:3)

CREATE OR REPLACE PROCEDURE stackoverflow([your_value] IN TYPE) AS
BEGIN
   UPDATE   [your_table] t
     SET t.[your_collumn] = [your_value]
   WHERE t.[your_collumn] != [your_value];
  COMMIT;


EXCEPTION
 [YOUR_EXCEPTION];

END stackoverflow;

答案 5 :(得分:1)

您需要在表格中使用唯一键id(假设它的值为1)才能执行以下操作:

UPDATE table1 SET col1="hello" WHERE id=1 AND col1!="hello"

答案 6 :(得分:1)

旧问题,但没有一个答案正确处理null值。

使用&lt;&gt;或者!=在比较差异值时会遇到麻烦,如果新值或旧值中存在潜在的空值,只有在更改时使用Postgres中的is distinct from运算符才能安全更新。详细了解here

答案 7 :(得分:0)

如果没有声明变量或类似变量,则很难引用插入的硬编码值。如果这是你的目标,你应该考虑Daniel Hilgarth的解决方案。通常你会掌握价值。在这种情况下,你就是这样做的。

declare @t table (col1 varchar(10))

insert @t values ('hello')
insert @t values ('bye')

declare @newvalueforallrows varchar(10)
set @newvalueforallrows = 'hello'

update @t set col1 = @newvalueforallrows
where col1 <> @newvalueforallrows

-- 1 row updated

答案 8 :(得分:0)

我认为这应该为你做的伎俩...

create trigger [trigger_name] on [table_name]
for insert 
AS declare  @new_val datatype,@id int;
select @new_val = i.column_name from inserted i;
select @id = i.Id from inserted i;
update table_name set column_name = @new_val
where table_name.Id = @id and column_name != @new_val;

答案 9 :(得分:-1)

update table1 set col1 = 'bye'
WHERE col1 != 'hello'