使用Insert Into将数据从一个表复制到另一个表

问题描述 投票:0回答:2

我有两个桌子。它们都具有相同的结构,除了table2具有附加的列。我目前使用存储的proc将数据从table1复制到table2,如下所示。但是,由于记录数量庞大(超过2000万),并且存储的proc的结构也很复杂,因此目前需要花费几个小时来运行。有没有人对如何优化代码有任何建议?谢谢!

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
2个回答
0
投票

首先,我要指出的是,您插入内容中的逻辑有缺陷。

使用@start从1开始,您总是跳过源表的第一行。然后在循环末尾添加1会导致它在循环的每次后续运行中跳过另一行。

如果您打算使用批量插入,我建议您阅读一下如何在MSSQLTips上使用它。

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

SELECT *

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

[ORDER BY

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%。

enter image description here

如果添加在源表的ID列上已排序的索引,则可以消除排序。现在,当循环运行时,它无需进行任何排序。

CREATE INDEX ix_Test ON #Test (Id)

enter image description here

其他研究方案

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

-1
投票

您逐行复制表格,这就是为什么要花这么长时间的原因。实现所需目标的最简单方法是将“ INSERT”与“ SELECT”语句结合使用。这样,您可以批量插入数据。

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'语句中使用星号,因为结果可能无法预测。

© www.soinside.com 2019 - 2024. All rights reserved.