改进INSERT INTO - FROM SELECT,SQL Query

时间:2009-02-12 15:05:38

标签: c# sql sql-server insert

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

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

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

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

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

我使用MSSQl 2005

THX

10 个答案:

答案 0 :(得分:8)

我发现,如果你有很多按顺序执行的INSERT语句,你可以通过在每个xxxx数量的insert语句之后添加一个'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

答案 1 :(得分:6)

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

  • BULK INSERT(TSQL)
  • SqlBulkCopy(。NET)
  • bcp(命令行)

如果 Where条款,我会检查它是否已被适当编入索引......

此外:

  • 可能在执行INSERT之前删除索引和触发器(之后重新创建)
  • 考虑删除整个表并使用SELECT INTO?(参见注释)

答案 2 :(得分:3)

好的,有一些基本问题。

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

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

  3. 聚簇索引 - 如果要将所有这些行插入到目标表中,并且它的聚簇索引(物理订单数据写入磁盘)不按顺序写入,则磁盘IO要求会通过顶层页面拆分和重新分配。一个简单的修复方法是在收件人表上创建一个连续种子密钥的聚簇索引。这通常可以确保您顺序写入表格,并且几乎总是在最后。

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

  5. 坦率地说,除了那个和其他一些建议之外,你很可能不会在交易中插入数百万行真正快速的插入。如果您通过批量插入执行此操作,它将会非常快,但从应用程序的角度来看可能不是您需要的。

答案 3 :(得分:2)

将您使用的CommandTimeout的{​​{1}}属性设置为合理值(10分钟或更长时间)。请记住,SqlCommand只需几秒钟。

答案 4 :(得分:1)

这里有一些好的答案。

就像添加一样,如果目标表上有索引,它们将减慢操作速度。但是,如果执行drop create技术,重建索引有时可能需要很长时间。

如果您不想删除索引,请在ORDER BY中使用与目标聚集索引匹配的SELECT,这似乎有所帮助(可能有助于最小化页面拆分)。

答案 5 :(得分:0)

您没有说明您使用此方法解决了什么问题。显然,WHERE会缩小记录集。但是如果不在New Table中修改结果集,那么为什么要复制数据呢?为什么不直接从Source查询?

答案 6 :(得分:0)

使用文件批量加载然后bcp / BULK INSERT或批量批量加载5K左右

答案 7 :(得分:0)

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

答案 8 :(得分:0)

我们过去使用的另一种方法是使用我们想要移动的主键创建临时表并使用while循环。这样你就可以采用一种块方式来做到这一点,这样你就可以避免大量的事务开销,如果你取消它并且必须回滚。

基本上你最终要做的是从表名中插入tablename(...)select(...)中的主键(从temptable中选择前10000键)

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

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

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

将@stop声明为int 设置@stop =(从tableName中选择count(primaryKey),其中primaryKey不在destinstiontable中)

while(@stop&gt; 0)   开始交易

插入到destinationTable(...)   从sourcetable中选择(...),其中primaryKey不在(从目标表中选择主键)

提交

设置@stop =(从tableName中选择count(primaryKey),其中primaryKey不在destinstiontable中) 端

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

所说的一些可能是你最好的选择。使用BCP,DTS或其他一些批量工具。如果你可以删除索引,它会使事情变得更快。

答案 9 :(得分:0)

您是否通过sql server management studio测试了sql,看看实际需要多长时间?我会从那里开始。 您可以改善选择的性能。 您可以使用插入的表格上的tablock提示来提高性能。