26

我有一个处理大量数据的存储过程。我将该数据插入到临时表中。事件的整体流程类似于

CREATE #TempTable (
    Col1    NUMERIC(18,0) NOT NULL,    --This will not be an identity column.
    ,Col2   INT NOT NULL,
    ,Col3   BIGINT,

    ,Col4   VARCHAR(25) NOT NULL,
    --Etc...

    --
    --Create primary key here?
)


INSERT INTO #TempTable
SELECT ...
FROM MyTable
WHERE ...

INSERT INTO #TempTable
SELECT ...
FROM MyTable2
WHERE ...

--
-- ...or create primary key here?

我的问题是在我的#TempTable 表上创建主键的最佳时间是什么时候?我推测我应该在插入所有数据后创建主键约束/索引,因为在创建主键信息时需要重新组织索引。但我意识到我强调的假设可能是错误的......

如果相关,我使用的数据类型是真实的。在#TempTable表中,Col1Col4组成我的主键。

更新:就我而言,我正在复制源表的主键。我知道构成我的主键的字段将始终是唯一的。如果我在最后添加主键,我不担心更改表失败。

尽管如此,我的问题仍然是假设两者都会成功,哪个更快

4

9 回答 9

17

取决于很多。

如果在加载后使主键索引聚集,整个表将被重写,因为聚集索引不是真正的索引,它是数据的逻辑顺序。您的插入执行计划将取决于计划确定时的索引,如果聚集索引到位,它将在插入之前排序。您通常会在执行计划中看到这一点。

如果您使主键成为一个简单的约束,它将是一个常规(非聚集)索引,并且该表将简单地以优化器确定的任何顺序填充并更新索引。

我认为(加载临时表的过程中)整体最快的性能通常是将数据写入堆,然后应用(非聚集)索引。

但是,正如其他人所指出的,索引的创建可能会失败。此外,临时表并不是孤立存在的。大概有一个最佳索引可以从中读取数据以进行下一步。该索引将需要到位或创建。 是您必须在此处权衡速度以换取可靠性(首先应用 PK 和任何其他约束)和稍后的速度(如果要拥有聚集索引,则至少要有一个聚集索引)的地方。

于 2009-06-22T22:20:42.163 回答
8

如果您的数据库的恢复模式设置为简单或批量记录,SELECT ... INTO ... UNION ALL 可能是最快的解决方案。SELECT .. INTO 是批量操作,批量操作的日志记录最少。

例如:

-- first, create the table
SELECT ...
INTO #TempTable
FROM MyTable
WHERE ...
UNION ALL
SELECT ...
FROM MyTable2
WHERE ...

-- now, add a non-clustered primary key:
-- this will *not* recreate the table in the background
-- it will only create a separate index
-- the table will remain stored as a heap
ALTER TABLE #TempTable ADD PRIMARY KEY NONCLUSTERED (NonNullableKeyField)

-- alternatively:
-- this *will* recreate the table in the background
-- and reorder the rows according to the primary key
-- CLUSTERED key word is optional, primary keys are clustered by default
ALTER TABLE #TempTable ADD PRIMARY KEY CLUSTERED (NonNullableKeyField) 

否则,Cade Roux 有很好的建议:之前或之后。

于 2009-06-23T01:37:13.873 回答
3

您也可以在插入之前创建主键 - 如果主键位于标识列上,那么插入无论如何都会按顺序完成,不会有任何区别。

于 2009-06-22T21:13:11.270 回答
2

比性能考虑更重要的是,如果您不是绝对 100% 确定您将有唯一值插入到表中,请先创建主键。否则主键创建失败。

这可以防止您插入重复/错误的数据。

于 2009-06-22T22:09:10.127 回答
1

如果您在创建表时添加主键,则第一个插入将是免费的(不需要检查。)第二个插入只需要查看它是否与第一个不同。第三个插入必须检查两行,依此类推。检查将是索引查找,因为存在唯一约束。

如果在所有插入之后添加主键,则每一行都必须与其他每一行匹配。所以我的猜测是尽早添加主键更便宜。

但也许 Sql Server 有一种非常聪明的方法来检查唯一性。因此,如果您想确定,请测量它!

于 2009-06-22T21:49:08.950 回答
1

我想知道是否可以改进一个非常“昂贵”的存储过程,该存储过程需要在跨表的每个插入时进行一堆检查,然后遇到了这个答案。在 Sproc 中,打开了几个临时表并相互引用。我在 CREATE TABLE 语句中添加了主键(即使我的选择使用 WHERE NOT EXISTS 语句来插入数据并确保唯一性)并且我的执行时间被大大缩短了。我强烈推荐使用主键。即使您认为不需要它,也请始终至少尝试一下。

于 2012-08-11T20:30:33.223 回答
0

我认为这对您的情况没有任何重大影响:

  • 要么您一次支付一点罚款,每次插入
  • 否则您将在所有插入完成后支付更大的罚款,但只有一次

当您在插入开始之前预先创建它时,如果 PK 值不是系统创建的,您可能会在插入数据时捕获 PK 违规。

但除此之外 - 没有太大区别,真的。

马克

于 2009-06-22T21:18:16.143 回答
0

我不打算回答这个问题,因为我对我对此的了解并不是 100% 有信心。但是因为看起来你没有得到太多回应......

我的理解是 PK 是一个唯一索引,当您插入每条记录时,您的索引就会更新和优化。所以……如果先添加数据,再创建索引,索引只优化一次。

因此,如果您确信您的数据是干净的(没有重复的 PK 数据),那么我会说插入,然后添加 PK。

但是如果你的数据可能有重复的PK数据,我会说先创建PK,所以它会尽快炸毁。

于 2009-06-22T21:59:10.320 回答
0

当您在创建表时添加 PK - 插入检查是O(Tn)(其中Tn是“第 n 个三角形数”,即1 + 2 + 3 ... + n),因为当您插入第 x 行时,它会针对先前插入的“x - 1”行进行检查

当您在插入所有值O(n^2)添加 PK 时 - 检查器是因为当您插入第 x 行时,它会针对所有n现有行进行检查。

第一个显然更快,因为O(Tn)小于O(n^2)

PS 示例:如果您插入 5 行,则为1 + 2 + 3 + 4 + 5 = 15操作与5^2 = 25操作

于 2018-05-14T11:31:15.563 回答