【发布时间】:2011-10-04 09:15:45
【问题描述】:
是否可以在sql中执行更新语句,但只有更新不同时才更新?
例如
如果在数据库中,col1 = "hello"
update table1 set col1 = 'hello'
不应执行任何类型的更新
如果
update table1 set col1 = "bye"
这应该执行更新。
【问题讨论】:
标签: sql sql-server sql-server-2005 tsql
是否可以在sql中执行更新语句,但只有更新不同时才更新?
例如
如果在数据库中,col1 = "hello"
update table1 set col1 = 'hello'
不应执行任何类型的更新
如果
update table1 set col1 = "bye"
这应该执行更新。
【问题讨论】:
标签: sql sql-server sql-server-2005 tsql
这可以通过更新前触发器实现。
在此触发器中,您可以将旧值与新值进行比较,如果它们没有差异,则取消更新。但这会导致调用者网站出现错误。
我不知道您为什么要这样做,但这里有几种可能性:
【讨论】:
ModifiedDate 值时,它会很有帮助。但我认为使用存储过程可以轻松完成,因为仅通过存储过程完成所有 DML 操作并不是一个坏主意。
如果您只想将字段更改为'hello',如果它是'bye',请使用:
UPDATE table1
SET col1 = 'hello'
WHERE col1 = 'bye'
如果您只想在与'hello' 不同的情况下更新,请使用:
UPDATE table1
SET col1 = 'hello'
WHERE col1 <> 'hello'
这种奇怪的方法有什么原因吗?正如丹尼尔所评论的那样,没有特别的收获 - 除非您有数千行 col1='hello'。是这样吗?
【讨论】:
如果新值与数据库中的值相同,则不执行任何更新
WHERE col1 != @newValue
(显然也应该有一些Id字段来标识一行)
WHERE Id = @Id AND col1 != @newValue
PS:本来你只想在值为'bye'时才进行更新,所以只需添加AND col1 = 'bye',但我觉得这是多余的,我只是假设
PS 2:(来自评论)另请注意,如果col1 是NULL,这不会更新值,所以如果NULL 是可能的,则设为WHERE Id = @Id AND (col1 != @newValue OR col1 IS NULL)。
【讨论】:
== 运算符?
WHERE Id = @Id AND (col1 != @newValue OR col1 IS NULL)
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;
【讨论】:
在查询编译和执行期间,SQL Server 不会花时间确定 UPDATE 语句是否会实际更改任何值。它只是按预期执行写入,即使是不必要的。
在这样的场景中
update table1 set col1 = 'hello'
您可能认为 SQL 不会做任何事情,但它会 - 它会执行所有必要的写入操作,就像您实际更改了值一样。物理表(或聚集索引)以及在该列上定义的任何非聚集索引都会发生这种情况。这会导致写入物理表/索引,重新计算索引和事务日志写入。处理大型数据集时,仅更新将收到更改的行具有巨大的性能优势。
如果我们想在不必要时避免这些写入的开销,我们必须设计一种方法来检查是否需要更新。检查是否需要更新的一种方法是添加类似“where col 'hello'。
update table1 set col1 = 'hello' where col1 <> 'hello'
但这在某些情况下效果不佳,例如,如果您要更新包含许多行的表中的多个列,并且这些行的一小部分实际上会更改其值。这是因为需要对所有这些列进行过滤,并且非相等谓词通常无法使用索引查找,以及上述表和索引写入以及事务日志条目的开销。
但是使用 EXISTS 子句和 EXCEPT 子句的组合有一个更好的选择。想法是将目标行中的值与匹配源行中的值进行比较,以确定是否确实需要更新。查看下面修改后的查询并检查以 EXISTS 开头的附加查询过滤器。请注意在 EXISTS 子句中 SELECT 语句如何没有 FROM 子句。这部分特别重要,因为这只会在查询计划中增加额外的常量扫描和过滤操作(两者的成本都是微不足道的)。因此,您最终得到的是一种非常轻量级的方法,用于确定是否首先需要 UPDATE,从而避免不必要的写入开销。
update table1 set col1 = 'hello'
/* AVOID NET ZERO CHANGES */
where exists
(
/* DESTINATION */
select table1.col1
except
/* SOURCE */
select col1 = 'hello'
)
当您使用文字值更新表中所有行的一个值时,这看起来过于复杂,而不是检查原始问题中简单场景的简单 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
)
【讨论】:
UPDATE users SET status=1; 可能会耗尽您的 IOPS(从而导致之后的高延迟),而 UPDATE users SET status=1 WHERE status<>1; 可以从内存缓存中得到满足,因此不会浪费任何宝贵的 IOPS。
select @param1, @param2, etc.
select table1.col1 except select col1 = 'hello' 可能会导致错误。因为select col1 = 'hello' 的结果是布尔值,所以不能从select col from table 的结果中排除布尔值,除非该结果也是布尔值。所以第二部分应该类似于... except select col1 from table1 where col1 = 'hello'。然后,我们将col1 类型的结果从另一个col1 类型的结果中排除。
我认为这应该对你有用...
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;
【讨论】:
您需要在表中使用唯一键 id(假设它的值为 1)来执行以下操作:
UPDATE table1 SET col1="hello" WHERE id=1 AND col1!="hello"
【讨论】:
老问题,但没有一个答案正确解决 null 值。
使用 或 != 如果在新值或旧值中存在潜在的空值以安全更新,则在比较差异值时使用 或 != 会遇到麻烦,仅在 Postgres 中使用 is distinct from 运算符进行更改。阅读更多关于它的信息here
【讨论】: