在SQL中,UPDATE总是快于DELETE + INSERT吗?

时间:2009-08-13 12:27:36

标签: sql sql-insert sql-delete

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

  1. ID:int,autoincremental(identity),primary key
  2. 名称:varchar(50),唯一,具有唯一索引
  3. 标签:int
  4. 我从不使用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已更改,但对我的应用程序无关紧要。

15 个答案:

答案 0 :(得分:60)

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

  1. 包含两个JDBC请求的事务控制器(生成父样本):删除和插入语句
  2. 包含Update语句的sepparate JDBC Request。
  3. 运行500次循环测试后,我得到了以下结果:

    DEL + INSERT - 平均:62ms

    更新 - 平均:30毫秒

    结果: Results

答案 1 :(得分:35)

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

此外,从商业角度来看,这是完全错误的。考虑理解该表上的名义审计线索要困难得多。


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

答案 2 :(得分:11)

同一行上的一个命令应始终比同一行上的两个命令快。所以UPDATE只会更好。

修改 设置表:

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

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

运行此命令,在我的系统(sql server 2005)上花费1秒钟:

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

答案 3 :(得分:9)

我担心你的问题正文与标题问题无关。

如果要回答标题:

  

在SQL中,UPDATE总是快于DELETE + INSERT吗?

然后回答是NO!

只是谷歌

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

与直接插入+更新相比,此类更新通过插入+更新导致更高成本(更多处理)的更新实现。

时就是这种情况
  • 使用唯一(或主要)密钥或
  • 更新字段
  • 当新数据在分配的更新前行空间(或甚至最大行大小)中不适合(更大)时,导致碎片,
  • 等。

我的快速(非详尽)搜索,而不是假装覆盖一个,给了我[1],[2]

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

答案 4 :(得分:5)

刚尝试在包含44个字段的表上更新43个字段,其余字段是主要的群集密钥。

更新耗时8秒。

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

彼得

MS SQL 2008

答案 5 :(得分:4)

请记住,发出DELETE + INSERT时发生的实际碎片与正确实现的UPDATE相反会在时间上产生很大的差异。

这就是为什么,例如,不支持MySQL实现的REPLACE INTO,而不是使用INSERT INTO ... ON DUPLICATE KEY UPDATE ...语法。

答案 6 :(得分:4)

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

更新

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

    (对于您正在更新的每一行重复此操作)

  7. 删除+插入:

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

      (这不重复,所有这些都可以在一个操作块中执行)。

    4. 使用Insert + Delete会将文件系统分段,但速度不快。在后台进行延迟优化将总是释放未使用的块并完全打包表。

答案 7 :(得分:3)

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

记住索引!

您已经定义了主键,它可能会自动成为聚簇索引(至少SQL Server会这样做)。集群索引表示根据索引将记录物理放置在磁盘上。 DELETE操作本身不会造成太大麻烦,即使一条记录消失后,索引保持正确。但是当您插入新记录时,数据库引擎必须将此记录放在正确的位置,在这种情况下会导致旧记录的某些“重新洗牌”为新记录“制造”。它会减慢操作的速度。

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

答案 8 :(得分:3)

如果你有几百万行怎么办?每行以一个数据开头,可能是客户名称。在为客户收集数据时,必须更新其条目。现在,让我们假设客户端数据的集合分布在许多其他机器上,稍后从中收集并放入数据库。如果每个客户端都有唯一的信息,那么您将无法执行批量更新;即,没有where子句标准供您一次更新多个客户端。另一方面,您可以执行批量插入。因此,问题可能更好地表现如下:执行数百万次单个更新是否更好,或者将它们编译为大型批量删除和插入更好。换句话说,代替“更新[表]设置字段=数据,其中clientid = 123”一个毫秒次,你做'从[表]删除clientid([所有客户端要更新]);插入[表]值(client1的数据),(client2的数据)等等

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

答案 9 :(得分:2)

显然,答案因您使用的数据库而异,但UPDATE总是可以比DELETE + INSERT更快地实现。由于内存操作无论如何都是微不足道的,在给定基于硬盘驱动器的数据库的情况下,UPDATE可以在hdd上就地更改数据库字段,而删除则会删除一行(留空空格),并插入新的行,也许到表的末尾(再次,它都在实现中)。

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

答案 10 :(得分:2)

如果没有特定的速度问题,速度问题就无关紧要了。

如果您正在编写SQL代码以对现有行进行更改,则更新它。其他任何事情都是不正确的。

如果你要打破代码应该如何工作的规则,那么你最好有一个该死的好的,量化的理由,而不是一个模糊的想法“这种方式更快”,当你不要我知道什么是“更快”。

答案 11 :(得分:1)

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

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

答案 12 :(得分:1)

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

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

对于非常少量的列或者如果更新整行,则删除+插入可能会更快,但FK约束问题很大。当然,也许你现在没有FK约束,但这总是真的吗?如果你有一个触发器,如果​​更新操作真的是更新,则编写处理更新的代码会更容易。

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

最后,如果您要更新记录,我建议您更新一条记录。然后检查数据库的性能统计信息和该表的统计信息,以查看是否有性能改进。其他任何事情都为时过早。

我工作的电子商务系统的一个例子:我们将信用卡交易数据存储在数据库中,分两步进行:首先,写一个部分交易来表明我们已经开始了这个过程。然后,当从银行更新授权数据时更新记录。我们可能已删除然后重新插入记录,但我们只是使用更新。我们的DBA告诉我们这个表是碎片化的,因为DB只为每一行分配了少量的空间,并且更新导致了块链接,因为它添加了大量数据。但是,我们只是将数据库调整为始终分配整行,而不是切换到DELETE + INSERT,这意味着更新可以使用预先分配的空白空间而没有任何问题。无需更改代码,代码仍然简单易懂。

答案 13 :(得分:0)

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

答案 14 :(得分:0)

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

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

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