在SQL中,UPDATE是否总是比DELETE + INSERT快?


72

假设我有一个包含以下字段的简单表:

  1. ID:int,自动递增(身份),主键
  2. 名称:varchar(50),唯一,具有唯一索引
  3. 标签:int

我从不使用ID字段进行查找,因为我的应用程序始终基于使用Name字段。

我需要不时更改Tag值。我正在使用以下简单的SQL代码:

UPDATE Table SET Tag = XX WHERE Name = YY;

我想知道是否有人知道以上方法是否总是比以下方法快?

DELETE FROM Table WHERE Name = YY;
INSERT INTO Table (Name, Tag) VALUES (YY, XX);

再说一遍-我知道在第二个示例中ID已更改,但对我的应用程序来说并不重要。


14
作为一种实践,我永远不建议更新主键列。
KM。

@KM:我同意,这是我的真实表的简化,其中所有查找都是在不是主键的唯一字符串字段上完成的。我确实有一个完全不相关的主键int值,因此我从示例中将其删除(它是自动创建的,完全不参与查找)
Roee Adler,2009年

@KM:我更新了示例以反映真实的表结构,以防万一它有所作为。
罗伊·阿德勒

4
UDPATE还具有不破坏表可能具有的任何外键关系的优点,只要所引用的键字段不变即可。如果您执行DELETE + INSERT,则可能会违反某些约束,因此DELETE可能会失败
marc_s 2009年

Answers:


75

这个答案有点太晚了,但是由于我遇到了类似的问题,所以我在同一台机器上使用了JMeter和MySQL服务器进行了测试:

  1. 包含两个JDBC请求的事务控制器(生成父示例):Delete和Insert语句
  2. 包含Update语句的单独的JDBC请求。

对500个循环运行测试后,我获得了以下结果:

DEL + INSERT-平均:62ms

更新-平均:30ms

结果: 结果


1
我很好奇看到具有大量数据集和索引的相同统计信息。
迈克尔·西尔弗

Michael,您好。该测试是在5年前针对localhost(在一台不存在的计算机上)进行的。为了提高准确性,应该在相同的表和索引表上重复进行该测试。最初的想法只是抓住性能差异的味道。如果时间允许,我可以重复并更新帖子。我很高兴,这个帖子仍然激发了ppl的好奇心:)
Dyptorden

41

表越大(列数和列大小),删除和插入而不是更新就变得越昂贵。因为您必须支付UNDO和REDO的价格。DELETE比UPDATE消耗更多的UNDO空间,并且REDO包含所需语句的两倍。

此外,从业务角度来看,这是完全错误的。考虑一下了解该表上的名义审计追踪将有多困难。


在某些情况下,涉及对表中所有行的批量更新,其中使用旧表中的CTAS创建新表的速度更快(将更新应用于SELECT子句的投影中),删除旧表并重命名新表。副作用是创建索引,管理约束和续订特权,但这值得考虑。


12

同一行上的一个命令应始终比同一行上的两个命令快。因此,仅UPDATE会更好。

编辑 设置表:

create table YourTable
(YourName  varchar(50)  primary key
,Tag int
)

insert into YourTable values ('first value',1)

运行此命令,这在我的系统上需要1秒钟(SQL Server 2005):

SET NOCOUNT ON
declare @x int
declare @y int
select @x=0,@y=0
UPDATE YourTable set YourName='new name'
while @x<10000
begin
    Set @x=@x+1
    update YourTable set YourName='new name' where YourName='new name'
    SET @y=@y+@@ROWCOUNT
end
print @y

运行此命令,这在我的系统上花费了2秒钟:

SET NOCOUNT ON
declare @x int
declare @y int
select @x=0,@y=0
while @x<10000
begin
    Set @x=@x+1
    DELETE YourTable WHERE YourName='new name'
    insert into YourTable values ('new name',1)
    SET @y=@y+@@ROWCOUNT
end
print @y

4
您是否基于任何具体数据?
罗伊·阿德勒

@Rax Olgud,您如何回答?您甚至都没有说过正在使用什么数据库。您提出了一个概念性问题,但需要具体数据。如果需要实际数据,则需要编写一个wile循环(在您的系统上),并更新该行1000次,然后编写另一个循环,将其删除/插入1000次。看看什么更快。
KM。

2
@Rax Olgud,删除和创建索引值以及检查任何约束都有些开销。如果您只是更新数据列,它将避免任何此类开销。
KM。

@ Mohy66,度量是运行所花费的时间,行计数的总和是用来验证测试期间完成的工作量。感谢您的不赞成投票。
KM。

8

恐怕您的问题与标题问题无关。

如果要回答标题:

在SQL中,UPDATE是否总是比DELETE + INSERT更快?

那么答案是否定的!

只是谷歌

  • “昂贵的直接更新” *“ sql server”
  • “延迟更新” *“ sql server”

这样的更新导致通过insert + update实现更新的成本比直接insert + update更昂贵(更多处理)。在这种情况下

  • 一个使用唯一(或主)键更新字段,或者
  • 当新数据不适合(更大)分配的更新前行空间(甚至最大行大小)时,会导致碎片,
  • 等等

我的快速(非穷举)搜索并不假装涵盖其中,给了我[1],[2]

[1]
更新操作
(Sybase®SQL Server性能和调优指南
第7章:SQL Server查询优化器)
http://www.lcard.ru/~nail/sybase/perf/11500.htm
[2]
UPDATE语句可能是复制为DELETE / INSERT对
http://support.microsoft.com/kb/238254


5

请记住,发出DELETE + INSERT而不是正确实现UPDATE时发生的实际碎片会随时间变化很大。

这就是为什么不鼓励使用MySQL实现的REPLACE INTO而不是使用INSERT INTO ... ON DUPLICATE KEY UPDATE ...语法的原因。


5

刚尝试用44个字段更新表中的43个字段,其余字段是主集群键。

更新花费了8秒钟。

“删除+插入”比“客户端统计信息”通过SQL Management Studio报告的最小时间间隔快。

彼得

微软SQL 2008


4

您的情况,我相信更新会更快。

记住索引!

您已经定义了主键,它可能会自动成为聚簇索引(至少SQL Server这样做)。群集索引是指根据索引将记录物理放置在磁盘上。即使删除一条记录,索引仍然保持正确,DELETE操作本身不会造成太大麻烦。但是,当您插入新记录时,数据库引擎将不得不将该记录放置在正确的位置,这在某些情况下会导致旧记录的“改组”为新记录“取代”。在那里会减慢操作速度。

如果值不断增加,则索引(尤其是聚集索引)效果最好,因此新记录仅会附加到尾部。也许您可以添加一个额外的INT IDENTITY列来成为聚集索引,这将简化插入操作。


1
“改组”将有页面拆分。
安德鲁

在此示例中,新记录将位于表的末尾(基于PK),因为用户未指定PK。如果“名称”索引是集群的,那将是一个问题,但是不太可能集群。
杰克·RG

4

没有特定的速度问题,速度问题是无关紧要的。

如果要编写SQL代码以更改现有行,请对其进行更新。还有其他不正确的地方。

如果您要打破代码应如何工作的规则,那么最好在没有代码的情况下有一个该死的,合理的量化理由,而不要含糊地说“这种方式更快”知道什么是“更快”。


没错,请注意。如果表有触发器,则选择delete + insert是不好的选择。
博士。BurakÖztürk19年

4

如果您有几百万行怎么办。每行以一条数据开头,也许是客户名称。在为客户收集数据时,必须更新其条目。现在,让我们假设客户数据的收集分布在许多其他机器上,之后再从其他机器中收集它们并将它们放入数据库中。如果每个客户端都有唯一的信息,则您将无法执行批量更新;即,没有条件可用于一次更新多个客户端。另一方面,您可以执行批量插入。因此,可能会提出以下更好的问题:执行数百万个单个更新是否更好,还是将它们编译为大容量的删除和插入更好?换句话说,不是“更新[表]设置字段=数据,其中clientid = 123”是一次,

是选择比另一个更好,还是两种方式都搞砸了?


(由作者删除)
杰克·RG

请参阅下面的相关答案。
杰克RG

3

删除+插入几乎总是更快,因为更新涉及更多步骤。

更新:

  1. 使用PK查找行。
  2. 从磁盘读取行。
  3. 检查哪些值已更改
  4. 使用填充的:NEW和:OLD变量提高onUpdate触发器
  5. 将新变量写入磁盘(整行)

    (对您要更新的每一行重复一次)

删除+插入:

  1. 将行标记为已删除(仅在PK中)。
  2. 在表末尾插入新行。
  3. 使用新记录的位置更新PK索引。

    (这不会重复,所有操作都可以在一个操作块中完成)。

使用Insert + Delete将使您的文件系统碎片化,但是速度不那么快。在后台执行惰性优化将始终释放未使用的块,并将表打包在一起。


15
这个答案过度简化了操作,并错过了主要商业RDBM模型的很多步骤-仅通过更改PK(而没有其他操作)删除行并不是主要商业RDBM的工作方式。您有关触发器的信息不正确,而且是单方面的。首先,删除/插入也可能会触发触发器-但您不能包括这些触发器。除非您指定每行触发器,否则它只会为更新触发一次,为删除/插入触发两次。
安德鲁(Andrew)

2

显然,答案因所使用的数据库而异,但实现UPDATE的速度总是快于DELETE + INSERT的速度。由于无论如何,内存操作通常都很琐碎,给定基于硬盘的数据库,UPDATE可以在HDD上就地更改数据库字段,而删除则可以删除行(保留空白空间)并插入新的行,也许到表的末尾(再次,全部在实现中)。

另一个较小的问题是,当您更新一行中的单个变量时,该行中的其他列保持不变。如果先删除然后再执行INSERT,则冒着忘记其他列并因此将其遗忘的风险(在这种情况下,您必须在DELETE之前执行SELECT才能临时存储其他列,然后再使用INSERT将其写回) 。


1
对于第一点,我不确定是否同意,特别是在使用可变长度字符串类型时。更新这些文件可能确实需要在“新位置”进行高清写入。
罗伊·阿德勒

1

这取决于产品。可以实现一种产品(在幕后)将所有UPDATE转换为(事务包装)DELETE和INSERT。假设结果与UPDATE语义一致。

我并不是说我知道有任何产品可以做到这一点,但这完全是合法的。


...完全合法,只要将外键约束检查推迟到插入之后再进行,这可能是不合法的。
Shiny先生和新安宇先生

我不确定,但是我听说SQL Server在内部对UPDATE执行DELETE + INSERT。如果是这样,那么对于SQL Server来说会有所不同吗?
法伊斯

@Faiz-与所有方法一样,唯一可以确定的方法是在您的环境中测试数据。这些操作的潜在成本不太可能成为您的瓶颈。使用SQL Server,如果您有触发器,则它肯定类似于删除/插入,但是是否正是系统实际要做的,谁需要知道:-)
Damien_The_Unbeliever,2009年

1

每次写入数据库都有很多潜在的副作用。

删除:必须删除一行,更新索引,检查外键,并可能级联删除,等等。插入:必须分配一行-这可能代替已删除的行,可能没有;更新:必须更新一个或多个值;必须更新索引,检查外键等。也许行的数据不再适合数据库的那个块,所以必须分配更多的空间,这可能会级联成多个要重写的块,或者导致碎片化的块;如果该值具有外键约束,则必须对其进行检查等。

对于极少数的列或整个行进行了更新,Delete + insert可能会更快,但是FK约束问题很大。当然,也许您现在没有FK约束,但这是否总是正确的?如果您有触发器,则在更新操作确实是更新的情况下,编写处理更新的代码会更容易。

要考虑的另一个问题是,有时插入和删除持有的锁与更新持有的锁不同。与您在更新记录时仅锁定单个记录相反,DB可能会在插入或删除时锁定整个表。

最后,如果您打算更新记录,我建议您仅更新记录。然后,检查数据库的性能统计信息以及该表的统计信息,以查看是否有性能需要改进。还有什么还为时过早。

我正在研究的一个电子商务系统的示例:我们正在分两步将信用卡交易数据存储在数据库中:首先,编写部分交易以表明我们已经开始了该过程。然后,当授权数据从银行返回时,更新记录。我们可以删除然后重新插入记录,但是我们只是使用了更新。我们的数据库管理员告诉我们表是零散的,因为数据库仅为每行分配少量空间,并且由于添加了很多数据,因此更新导致了块链接。但是,我们没有调整数据库以始终分配整个行,而是切换到DELETE + INSERT,这意味着更新可以使用预分配的空白空间而不会出现问题。无需更改代码,并且代码保持简单易懂。


1

我的场景是大量的个人更新与批量删除/批量插入。我有过去几年中多个客户的历史销售数据。在获得验证数据之前(下个月的15日),我将每天调整销售数字以反映从其他来源获得的当前状态(这意味着每个客户每天最多覆盖45天的销售)。可能没有变化,或者可能有一些变化。我可以对逻辑进行编码以找到差异并更新/删除/插入受影响的记录,或者我可以删除昨天的数字并插入今天的数字。显然,后一种方法更简单,但是如果由于搅动而破坏表的性能,那么它

因此,我要替换记录,并且旧记录和新记录之间可能存在某些关系,但是总的来说,我不一定要将旧数据与新数据进行匹配(这是一个额外的步骤,会导致删除,更新和插入)。同样,将更改相对较少的字段(最多20个中的7个或15个中的2个)。

可能会一起检索的记录将同时插入,因此在物理上应彼此靠近。这是否可以弥补由于该方法造成的混乱而造成的性能损失,并且比所有这些单独记录更新的撤消/重做成本更好吗?


0

在特定情况下,Delete + Insert可以节省您的时间。我有一个具有30000个奇数行的表,并且使用数据文件每天更新​​/插入这些记录。上载过程将生成95%的更新语句,因为记录已经存在,而5%的插入语句则不存在。或者,将数据文件记录上载到临时表中,删除临时表中记录的目标表,然后从临时表中插入目标表,则显示时间增加了50%。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.