【问题标题】:Copying data from one table to another using Insert Into使用 Insert Into 将数据从一个表复制到另一个表
【发布时间】:2020-04-20 18:19:49
【问题描述】:

我有两张桌子。它们都具有相同的结构,除了 table2 有一个附加列。我目前使用存储过程将数据从 table1 复制到 table2 中,如下所示。

但是,由于记录的绝对数量(超过 2000 万)以及存储过程的结构,目前这需要几个小时才能运行。

有人对如何优化代码有任何建议吗?

CREATE PROCEDURE dbo.insert_period @period INT AS

DECLARE @batchsize INT
DECLARE @start INT
DECLARE @numberofrows INT

SELECT @numberofrows = COUNT(*) from daily_table

SET @batchsize = 150000
SET @start = 1

WHILE @start < @numberofrows
BEGIN
    INSERT INTO dbo.main_table WITH (TABLOCK) (
    col1,
    col2,
    ....,
    col26,
    time_period
    )
    SELECT *, @period FROM dbo.daily_table
    ORDER BY id

    OFFSET @start ROWS
            FETCH NEXT @batchsize ROWS ONLY

    SET @start += @batchsize + 1
END

我在这里使用的 id 不是唯一的。表本身没有任何键或唯一 ID。

【问题讨论】:

    标签: sql sql-server stored-procedures sql-insert


    【解决方案1】:

    首先我想指出您插入的逻辑有缺陷。

    @start 从 1 开始,您总是会跳过源表的第一行。然后在循环结束时向其添加 1 会导致它在每次后续循环运行时跳过另一行。

    如果您设置使用批量插入,我建议您在 MSSQLTips 上阅读它的工作原理。

    为了帮助您提高性能,我建议您查看以下内容:

    选择 *

    删除SELECT * 并替换为列名。这将帮助优化器为您提供更好的查询计划。可以在此SO Question 中找到有关为什么 SELECT * 不好的更多信息。

    订购方式

    ORDER BY 可能会拖慢您的速度。如果没有看到您的查询计划,我们无法确定。每次执行循环时,它都会查询源表并且必须对所有这些记录进行排序。对 20 多条铣削记录进行多次排序是一项繁重的工作。看看下面我的简化示例。

    CREATE TABLE #Test (Id INT);
    INSERT INTO #Test VALUES (1), (2), (3), (4), (5);
    
    DECLARE @batchsize INT;
    DECLARE @start INT;
    DECLARE @numberofrows INT;
    
    SELECT  @numberofrows = COUNT(*) FROM   #Test;
    
    SET @batchsize = 2;
    SET @start = 0;
    
    WHILE @start < @numberofrows
    BEGIN
        SELECT
            *
            , 10
        FROM
            #Test
        ORDER BY
            Id OFFSET @start ROWS FETCH NEXT @batchsize ROWS ONLY;
    
        SET @start += @batchsize;
    END;
    

    以下是示例生成的查询计划的一部分。请注意以黄色突出显示的排序操作。它的成本占该查询计划的 78%。

    如果我们添加一个已经在源表的 Id 列上排序的索引,我们可以消除排序。现在,当循环运行时,它不必进行任何排序。

    CREATE INDEX ix_Test ON #Test (Id)
    

    其他研究选择

    1. Columnstore Indexes
    2. Batch Mode in RowStore
    3. Parallel Inserts

    【讨论】:

      【解决方案2】:

      您逐行复制表格,这就是它需要这么长时间的原因。实现您想要的最简单的方法是“插入”与“选择”语句相结合。这样,您可以将数据插入一批。

      CREATE TABLE dbo.daily_table (id INT PRIMARY KEY IDENTITY,
                                    value1 NVARCHAR(100) NULL,
                                    value2 NVARCHAR(100) NULL);
      GO
      
      CREATE TABLE dbo.main_table (id INT PRIMARY KEY IDENTITY,
                                   value1 NVARCHAR(100) NULL,
                                   value2 NVARCHAR(100) NULL,
                                   value3 NVARCHAR(100) NULL);
      GO
      
      INSERT INTO dbo.daily_table (value1, value2)
      VALUES('1', '2');
      
      -- Insert with Select
      INSERT INTO dbo.main_table (value1, value2)
      SELECT  value1,     value2
      FROM    dbo.daily_table;
      

      此外,最好不要在“SELECT”语句中使用星号,因为结果可能无法预测。

      【讨论】:

      • OP 没有逐行复制表格。正在分批进行。
      • @ChrisAlbert,我没有足够注意阅读查询,感谢您指出这一点。
      猜你喜欢
      • 2019-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-12
      • 2013-03-16
      • 2015-08-15
      • 2012-11-24
      相关资源
      最近更新 更多