6

目前我得到了编程生成的这种类型的查询(c#)

INSERT INTO TableName (Field1, Field2, Field3)
SELECT Field1, Field2, Field3 FROM TableName2

问题是 SELECT 可以有很多记录的结果(比如一百万),所以它需要很多次,结果是连接超时。

此外,如果我将所有插入分成单个插入(对于这个例子,一百万个插入查询),执行需要很长时间......但它工作......

有没有办法改进这种类型的查询?

我使用 MSSQl 2005

谢谢

4

10 回答 10

8

我发现,如果您有很多按顺序执行的 INSERT 语句,您可以通过在每个 xxxx 个插入语句后添加一个“GO”语句来提高性能:

...
INSERT INTO Table ( ... ) VALUES ( ... )
INSERT INTO Table ( ... ) VALUES ( ... )
INSERT INTO Table ( ... ) VALUES ( ... )
GO
INSERT INTO Table ( ... ) VALUES ( ... )
INSERT INTO Table ( ... ) VALUES ( ... )
...

另一种可能性可能是确保您的 INSERT INTO .. SELECT FROM 查询不会一次插入所有内容,而是使用某种分页技术:

INSERT INTO Table ...
SELECT ...
FROM OtherTable WHERE Id > x and Id < y
于 2009-02-12T15:10:24.627 回答
6

好吧,如果它是完整的副本,我想知道您是否不应该研究批量加载工具?

  • 批量插入 (TSQL)
  • SqlBulkCopy (.NET)
  • bcp(命令行)
  • ETC

如果你一个Where子句,我会检查它是否被适当索引......

此外:

  • 可能在执行 INSERT 之前删除索引和触发器(之后重新创建)
  • 考虑删除整个表并使用 SELECT INTO ?(看评论)
于 2009-02-12T15:08:54.737 回答
3

好的,有一些基本问题。

  1. I\O - 如果表不在单独的磁盘上,则在从另一个表读取时插入到表中很可能会导致磁盘争用。将相对的桌子放在物理上不同的主轴上。

  2. 事务日志 - 您需要确保您的事务日志在它自己的磁盘上,或者在较小的事务中工作(一次几千行)或使用未记录的 BCP\Bulk Insert。

  3. 聚集索引 - 如果您将所有这些行插入到目标表中,并且它的聚集索引(将物理订单数据写入磁盘)没有按顺序写入,那么磁盘 IO 需求会因为页面拆分和重新写入而达到顶峰分配。一个简单的解决方法是在收件人表上创建一个聚集索引,该索引是一个顺序种子键。这通常会确保您在表中获得顺序写入,并且几乎总是在最后。

  4. 文件扩展 - 确保您已设置 SQL 以适当的速度扩展文件,例如 10% 左右。否则它将不得不不断调整文件大小并将磁盘归零。还有一些方法可以防止它不得不将磁盘清零,例如在您的组策略中为 Sql Service 用户启用批量文件操作权限。

坦率地说,除了这个和其他一些建议之外,您不太可能在事务中真正快速地插入数百万行。如果您通过 Bulk Insert 执行此操作,它会快得多,尽管从应用程序的角度来看它可能不是您需要的。

于 2009-02-12T19:07:25.833 回答
2

将您正在使用的CommandTimeout属性设置为合理的值(10 分钟或其他时间)。SqlCommand请记住,CommandTimeout以秒为单位。

于 2009-02-12T15:09:15.470 回答
1

这里有一些很好的答案。

就像补充一点,如果您在目标表上有索引,它们会减慢操作速度。但是,如果您使用 drop create 技术,重建索引有时可能需要很长时间。

ORDER BY如果您不想删除索引,请使用与目标SELECT聚集索引匹配的索引,这似乎有帮助(可能有助于最大限度地减少页面拆分)。

于 2009-02-12T16:31:42.450 回答
0

你没有指出你用这种方法解决了什么问题。显然,一个 WHERE 会缩小记录集。但是如果结果集不会在新表中被修改,那么为什么要复制数据呢?为什么不直接从Source查询?

于 2009-02-12T15:11:31.457 回答
0

要么通过使用文件进行批量加载,然后使用 bcp/BULK INSERT 进行批量加载,要么以 5K 左右的批量进行批量加载

于 2009-02-12T15:11:52.233 回答
0

首先,永远不要尝试通过 C# 插入一百万条记录。永远不要一次处理一大组记录。这是应该由数据库在数据库中完成的工作。使用批量插入或 SSIS 或 DTS 来执行此操作。然后将其安排为非工作时间的工作。如果它仍然需要太长时间,那么我建议您以数千个批次运行它(您必须使用自己的数据库来查看最佳选择,因为您可以安全处理的数字很大程度上取决于表,索引如何你的服务器速度很快,有多少用户也在尝试对同一张表进行工作。

于 2009-02-12T15:13:39.140 回答
0

我们过去使用的另一种方法是使用我们要移动的主键创建一个临时表并使用 while 循环。通过这种方式,您可以以一种块方式执行此操作,这样您就可以避免在取消并且必须回滚时产生大量事务开销。

基本上你最终要做的是插入表名(...)从表名中选择(...),其中主键在(从临时表中选择前 10000 个键)

您想要在辅助结果集中的前 10000 个,以便您可以将它们从临时表中删除,这样它们就不会再次被处理。

另一种方法是使用游标来减少一次处理的记录数。

另一种循环方法是在 while 循环中执行类似的操作。

将 @stop 声明为 int set @stop = (select count(primaryKey) from tableName where primaryKey not in destinationtable)

while (@stop > 0) 开始事务

插入destinationTable (...) select (...) from sourcetable where primaryKey not in (select primarykey from destinationtable)

犯罪

set @stop = (select count(primaryKey) from tableName where primaryKey not in destinationtable) end

不是最有效的,但它会起作用,并且应该允许您保留事务日志。除非您需要它,否则请确保使用 no lock 关键字,以便在执行此大型移动时不会阻止其他事务(除非您使用 BCP 或 DTS,因为它们要快得多)。

不过,有些话可能是你最好的选择。使用 BCP、DTS 或其他一些批量工具。如果您可以删除索引,它将使事情进展得更快。

于 2009-02-12T16:26:29.497 回答
0

你有没有通过sql server management studio 测试过sql,看看实际需要多长时间?我会从那里开始。您可以提高选择的性能。并且您可以通过插入表格上的 tabblock 提示来提高性能。

于 2009-02-12T19:19:55.320 回答