16

我有一个存储过程,它通过查询将 750K 记录放入临时表中,这是它的第一个操作之一。如果我在填充临时表之前在临时表上创建索引,则与填充表后索引时相比,该项目的运行时间大约是两倍。(索引是单列中的整数,被索引的表只有两列,每列一个整数。)

这对我来说似乎有点不对劲,但是我对引擎盖下发生的事情没有最坚定的理解。有人对此有答案吗?

4

9 回答 9

42

如果创建聚集索引,它会影响数据在磁盘上的物理排序方式。最好在事后添加索引,让数据库引擎在知道数据如何分布时对行重新排序。

例如,假设您需要用编号的砖块建造一堵砖墙,以便编号最大的砖块位于墙的底部。如果你只是按随机顺序递砖,一次一个,这将是一项艰巨的任务——你不知道哪些砖会是最高编号的,你必须把墙拆掉并一遍又一遍地重建它。如果你把所有的积木都摆在你面前,并且可以组织你的工作,那么处理这项任务会容易得多。

数据库引擎就是这样——如果你让它知道整个工作,它会比你一次只喂它一行更有效率。

于 2008-08-26T19:19:27.710 回答
6

这是因为每次插入新行时数据库服务器都必须进行计算。基本上,您最终每次都会重新索引表。这似乎不是一项非常昂贵的操作,也不是,但是当您将其中的许多操作一起进行时,您就会开始看到影响。这就是为什么您通常希望在填充行之后进行索引,因为这只是一次性成本。

于 2008-08-26T19:20:57.800 回答
3

如果您要在之后立即大量加载空表,则永远不要在空表上创建索引。当表上的数据发生变化时,必须维护索引,因此想象一下,对于表上的每个插入,索引都被重新计算(这是一项昂贵的操作)。先加载表,加载完成后创建索引。这就是性能差异。

于 2008-08-26T19:17:34.173 回答
3

这样想吧。

给定
unorderedList = {5, 1,3}
orderedList = {1,3,5}

将 2 添加到两个列表中。
unorderedList = {5, 1,3,2}
有序列表 = {1,2,3,5}

您认为哪个列表更容易添加?

顺便说一句,在加载之前订购您的输入会给您带来提升。

于 2008-08-26T19:22:08.873 回答
2

执行大型数据操作操作后,您经常需要更新底层索引。您可以使用 UPDATE STATISTICS [table] 语句来做到这一点。

另一种选择是删除并重新创建索引,如果您正在执行大量数据插入,则可能会更快地执行插入。您甚至可以将其合并到您的存储过程中。

于 2008-08-26T19:19:15.463 回答
1

这是因为如果您插入的数据不是按索引的顺序排列的,SQL 将不得不拆分页面为额外的行腾出空间,以使它们在逻辑上保持在一起

于 2008-08-26T19:16:28.747 回答
1

这是因为当 SQL Server 使用数据索引表时,它能够生成索引列中值的准确统计信息。在某些时候 SQL Server 会重新计算统计信息,但是当您执行大量插入时,值的分布可能会在上次计算统计信息后发生变化。

可以在查询分析器上发现统计信息已过时的事实。当您看到在某个表扫描中,预期的行数与实际处理的行数相差很大。

插入所有数据后,您应该使用UPDATE STATISTICS重新计算值的分布。之后,不应观察到性能差异。

于 2008-08-26T19:21:50.373 回答
1

如果您在表上有索引,当您向表中添加数据时,SQL Server 将不得不重新排序表,以便在适当的位置为新记录腾出空间。如果您要添加大量数据,则必须一遍又一遍地对其进行重新排序。通过仅在加载数据后创建索引,重新排序只需发生一次。

当然,如果您是按索引顺序导入记录,则无关紧要。

于 2008-08-26T19:22:14.553 回答
1

除了索引开销之外,出于同样的原因,将每个查询作为事务运行是一个坏主意。如果您在 1 个显式事务中运行插入块(例如 100 个),您还应该看到性能提升。

于 2008-08-26T23:38:03.283 回答