对于表的大量更改,有什么更好的方法:DELETE 和 INSERT 每次或 UPDATE 存在?

ado*_*lot 30 performance sql-server-2005 sql-server application-design fragmentation

我正在做一个项目,我需要每天在一张表中更改大约 36K 条记录。我想知道什么会表现得更好:

  1. 删除行并插入新行,或
  2. 更新已经存在的行

对我来说,删除所有行并插入新行会更容易,但如果这会使表和索引碎片化并影响性能,那么我更愿意在可能的情况下进行更新并仅在必要时删除/插入。

这将是一项夜间服务,我不希望提高流程本身的速度。我更关心对这个表的查询性能,我已经有 8900 万条记录,以及这个每晚的过程将如何影响它。

对于这个夜间流程,我应该删除/插入记录还是应该更新现有记录(在可能的情况下)?

Dha*_*DK' 13

这实际上取决于有多少数据正在发生变化。假设这个表有 20 列。而且您还有 5 个索引 - 每个索引都在一个差异上。柱子。

现在,如果所有 20 列中的值都在更改或即使 5 列中的数据发生更改并且这 5 列都已编入索引,那么“删除和插入”可能会更好。但是,如果只有 2 列正在更改,并且可以说它们不是任何非聚集索引的一部分,那么您最好“更新”记录,因为在这种情况下,只会更新聚集索引(并且索引不必更新)更新)。


在进一步的研究中,我确实发现我的上述评论有点多余,因为 SQL Server 内部有 2 个单独的机制来执行更新。- “就地更新”(即通过将列值更改为原始行中的新值)或作为“非就地更新”(删除后跟插入)。

就地更新是规则,并在可能的情况下执行。此处,行在同一范围内完全位于同一页面上的同一位置。仅更改受影响的字节。tlog 只有一条记录(假设没有更新触发器)。如果正在更新堆(并且页面上有足够的空间),则会就地进行更新。如果集群键发生变化但行根本不需要移动,更新也会就地发生。

例如:如果你有一个关于姓氏的聚集索引并且你有以下名字:Able、Baker、Charlie 现在你想将 Baker 更新为 Becker。无需移动任何行。所以这可以就地进行。然而,如果您必须将 Able 更新为 Kumar,则必须移动行(即使它们位于同一页面上)。在这种情况下,SQL Server 将执行 DELETE,然后执行 INSERT。

考虑到上述情况,我建议您进行常规 UPDATE 并让 SQL Server 找出如何在内部执行此操作的最佳方法。

有关“更新”内部结构或任何 SQL Server 相关内部结构的更多详细信息,请查看 Kalen Delaney、Paul Randal 等人的书 - SQL Server 2008 Internals


dat*_*god 9

您是否研究过SQL 2008 中的MERGE命令?这是一个基本示例:

  merge YourBigTable ybt
  using (select distinct (RecordID) from YourOtherTable) yot
     on yot.Recordid = YBT.RecordID
  when NOT matched by target
  then  insert (RecordID)
        values (yot.DeviceID) ;
Run Code Online (Sandbox Code Playgroud)

这基本上是一个“UPSERT”命令。如果存在则更新,如果不存在则插入。非常快,非常酷的命令。

  • 如果您知道是这种情况,请证明这一点:) (4认同)

小智 6

但是,我自己在一个有 3000 万(3 千万)条记录的表上检查了删除和插入 vs 更新。该表有一个聚集唯一复合键和 3 个非聚集键。对于删除和插入,需要 9 分钟。更新需要 55 分钟。每行中只有一列被更新。

所以,我请你们不要猜测。当处理包含许多列和大量数据的大表时,方程会发生变化。


Ask*_*ken 5

更新没有那么快。实现快速插入的诀窍是在插入数据时禁用索引。

考虑使用这个:

-- disable indexes
ALTER INDEX [index_name] ON dbo.import_table DISABLE
-- ... disable more indexes

-- don't use delete if you don't care about minimal logging. truncate is faster
TRUNCATE TABLE dbo.import_table

-- just insert the new rows
INSERT dbo.import_table
SELECT
    *
FROM
    dbo.source_table

-- rebuild indexes
ALTER INDEX [index_name] ON dbo.import_table REBUILD
-- ... rebuild more indexes
Run Code Online (Sandbox Code Playgroud)

更快的是在 db 选项中关闭自动统计更新。如果表发生重大变化,您应该运行:

UPDATE STATISTICS dbo.import_table
Run Code Online (Sandbox Code Playgroud)

或者

EXEC sp_updatestats
Run Code Online (Sandbox Code Playgroud)

作为定期工作(每天,每周,取决于数据库大小)以保持最新统计数据。需要注意的是在表为空时更新统计信息。如果您在再次填充表后不运行它,那将会搞砸统计数据。

  • 我不同意这种情况总是如此。此外,@adopilot 问题中的表不能被 TRUNCATE 清除,因为它包含 89m 记录,而他只想更新 36k。 (4认同)