【问题标题】:SQL Server inserting huge number of rows to a table with default values and identity column in itSQL Server向其中包含默认值和标识列的表插入大量行
【发布时间】:2013-12-01 12:36:19
【问题描述】:

我需要在一个有 2 列的表中插入大约 6400000 行

CREATE TABLE [DBName].[DBO].[BigList] 
(
[All_ID] [int] identity(1,1) NOT NULL, 
[Is_It_Occupied] [int] default(0) not null 
)

我今天使用下面的代码,耗时很长,大概100分钟左右。

    SET @NumberOfRecordsToInsert = 6400000;
WHILE (@NumberOfRecordsToInsert > 0)
BEGIN
    INSERT [DBName].[DBO].[BigList] DEFAULT VALUES;
    SET @NumberOfRecordsToInsert = @NumberOfRecordsToInsert - 1
END

有没有人有更好的方法来做到这一点?

【问题讨论】:

  • 这张表有索引吗?
  • 您考虑过将 Is_It_Occupied 声明为 BIT 吗?
  • 你的数据库的 FILL FACTOR 怎么样?
  • 数据库是否足够大以容纳表而不会在插入期间自动增长?在开始插入过程之前,您可以通过预先分配足够的空间来保存整个表的数据(和日志)来提高性能。如果您在插入发生时监视服务器(使用 perfmon 等),您可以观察“日志增长”和“数据库增长”事件等,以查看它们是否经常发生(并对性能产生不利影响)。

标签: sql-server sql-server-2008 insert identity-column


【解决方案1】:

从某处抓取 6400000 行,并一次将它们全部插入。

insert into BigList(Is_It_Occupied)
select top(6400000) 0
from sys.all_objects as o1
  cross join sys.all_objects as o2
  cross join sys.all_objects as o3

测试了不同解决方案在我的计算机上运行的时间。

Solution                                           Seconds
-------------------------------------------------- -----------
Mikael Eriksson                                    13
Naresh                                             832
Dd2                                                25
TToni                                              92
Milica Medic                                       90
marc_s                                             2239

【讨论】:

    【解决方案2】:

    您的主要问题是每个语句都在单独的事务中运行。将所有内容放在一个事务中是不可取的,因为非常大的事务会产生它们自己的问题。

    但您的代码中最大的瓶颈将是事务日志上的 I/O。以下代码在我的笔记本电脑(使用三星 840 SSD)上实现了 14 MB/s 的整体写入速率,并在 75 秒内运行:

    DECLARE @NumberOfRecordsToInsert INT = 6400000;
    DECLARE @Inner INT = 10000;
    SET NOCOUNT ON
    WHILE (@NumberOfRecordsToInsert > 0)
    BEGIN
        BEGIN TRAN
          SET @Inner = 0
          WHILE (@Inner < 10000)
          BEGIN
            INSERT [BigList] DEFAULT VALUES;
            SET @Inner = @Inner+1
          END
        COMMIT
        SET @NumberOfRecordsToInsert = @NumberOfRecordsToInsert - @Inner
    END
    

    【讨论】:

      【解决方案3】:

      你为什么不用这个:

      INSERT [DBName].[DBO].[BigList] DEFAULT VALUES;
      GO 6400000
      

      在 SQL Server Management STudio 中,这将按照您在 GO xxxx 语句中指定的次数执行命令

      但即便如此:插入超过 600 万行需要一些时间!

      【讨论】:

      • 我在 Management Studio 中输入了这个,它显示了不正确的语法错误
      • @Naresh:那么:您使用的是什么版本 SQL Server 以及哪个版本(Express、Web、Standard 等)?您是否在新的空白查询页面中输入了此内容?还是您只突出显示这两行来执行它们?它对我来说很好用 - SQL Server 2008 R2 或 2012,开发人员版。
      • SQL Server 2008。此外,我必须再次在存储过程中使用该语句。所以,GO 对我来说可能不是最佳选择。
      • @Naresh:它在存储过程中不起作用,因为GO 不是 SQL 关键字 - 它是 SQL Server Mgmt Studio 特定的分隔符。如果你在 Mgmt Studio 中运行它就可以正常工作(你从来没有提到过这段代码必须在存储过程中执行!)
      • @Learning:那些在那个表上定义的——你可以在你的表定义中为每一列定义默认值——或者如果没有指定,SQL Server 将使用它的每列都有自己的默认值(例如 int 列的 0 等)
      【解决方案4】:

      您可以尝试这样的事情,它比运行您的查询花费的时间更少:

       SET NOCOUNT ON 
       BEGIN TRAN 
       DECLARE @i INT 
       SET @i = 1 
       WHILE @i <= 6400000 
       BEGIN 
       INSERT INTO [DBName].[DBO].[BigList] DEFAULT VALUES 
       SET @i = @i + 1 
       END 
       COMMIT TRAN
      

      希望对你有帮助

      【讨论】:

      • 这可行,但需要大约 100 分钟。所以需要一些不同的东西
      【解决方案5】:
      DECLARE @NoRows INT
      DECLARE @temp AS TABLE (Is_It_Occupied INT)
      
      SET @NoRows = 1000
      WHILE (@NoRows > 0)
      BEGIN
          INSERT INTO @temp (Is_It_Occupied) VALUES (0)
          SET @NoRows = @NoRows - 1
      END
      
      SET @NoRows = 6400
      WHILE (@NoRows > 0)
      BEGIN
          INSERT INTO BigList (Is_It_Occupied)
          SELECT Is_It_Occupied FROM @temp
          SET @NoRows = @NoRows - 1
      END
      

      【讨论】:

        猜你喜欢
        • 2012-03-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-11
        • 2015-11-11
        • 2021-08-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多