【问题标题】:Improve INSERT INTO - FROM SELECT, SQL Query改进 INSERT INTO - FROM SELECT、SQL 查询
【发布时间】:2009-02-12 15:05:37
【问题描述】:

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

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

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

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

有什么方法可以改进这种类型的查询吗?

我使用 MSSQl 2005

谢谢

【问题讨论】:

    标签: c# sql sql-server insert


    【解决方案1】:

    我发现,如果您有很多按顺序执行的 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
    

    【讨论】:

      【解决方案2】:

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

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

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

      另外:

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

      【讨论】:

      • 不要选择进入... 它会锁定系统表,直到查询完成,因为该表是在同一个事务中创建的。无论如何,它不会带来任何性能提升。
      【解决方案3】:

      好的,有一些基本问题。

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

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

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

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          这里有一些很好的答案。

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

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

          【讨论】:

            【解决方案6】:

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

            【讨论】:

              【解决方案7】:

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

              【讨论】:

                【解决方案8】:

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

                【讨论】:

                • 而且,他正在使用数据库。 C# 代码只是启动查询。
                【解决方案9】:

                我们过去使用的另一种方法是创建一个临时表,其中包含我们要移动的主键并使用 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 indestinationtable) 结束

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

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

                【讨论】:

                • 仅使用键创建临时表将与 tempdb 挂钩,只会使 IO 问题变得更糟。
                • 确实如此。在这种情况下,我仍然更喜欢使用 DTS 或其他一些 BCP 工具。尝试手动完成可能会很痛苦并且需要大量资源。
                【解决方案10】:

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2022-08-02
                  • 2012-01-23
                  • 2010-10-19
                  • 2020-07-13
                  • 2011-01-29
                  相关资源
                  最近更新 更多