如何将大量数据从一张表复制到同一数据库中的另一张表?

Bal*_*ala 3 sql-server sql-server-2014

我在同一个数据库中有两个具有相同列结构的表:TableATableB.

TableA没有任何索引,但TableB有一个非聚集唯一索引。

TableA有 2.9 亿行数据需要复制到TableB.

由于它们都具有相同的结构,我已经尝试过

INSERT INTO TableB 
    SELECT * 
    FROM TableA;
Run Code Online (Sandbox Code Playgroud)

它执行了几个小时,并产生了一个巨大的日志文件,填满了磁盘。结果磁盘空间不足,查询被终止。

我可以缩小日志文件。如何有效地将这些多行数据复制到另一个表?

Phi*_* P. 5

首先,TableB在插入行之前禁用索引。您可以使用 T-SQL 来做到这一点:

ALTER INDEX IX_Index_Name ON dbo.TableB DISABLE;  
Run Code Online (Sandbox Code Playgroud)

确保禁用目标表上的所有约束(外键、检查约束、唯一索引)。

加载完成后重新启用(并重建)它们。

现在,有几种方法可以解决这个问题:

  1. 您必须接受数据丢失的轻微机会:使用INSERT INTO ... SELECT ... FROM ...您拥有的语法,但首先将数据库切换到大容量日志恢复模式(切换阅读)。如果您已经处于 Bulk-logged 或 Simple 状态,则无济于事。
  2. 先导出数据:您可以使用 BCP 实用程序导出/导入数据。支持批量加载数据。在此处阅读有关使用 BCP 实用程序的更多信息。
  3. 花哨,首先导出数据:使用 SQL 2012+,您可以尝试将数据导出到二进制文件(使用 BCP 实用程序)并使用BULK INSERT语句和设置ROWS_PER_BATCH选项加载它。
  4. 老派的“我不在乎”方法:为了防止日志填满,您需要分批执行插入,而不是一次执行所有插入。如果您的数据库在完全恢复模式下运行,您将需要保持日志备份运行,甚至可能尝试增加作业的频率。

    要批量加载您的行,您将需要一个WHILE(不要在日常工作中使用它们,仅用于批量加载),如果您在dbo.TableA 表中有标识符,则以下内容将起作用:

    DECLARE @RowsToLoad BIGINT;
    DECLARE @RowsPerBatch INT = 5000;
    DECLARE @LeftBoundary BIGINT = 0;
    DECLARE @RightBoundary BIGINT = @RowsPerBatch;
    
    SELECT @RowsToLoad = MAX(IdentifierColumn) dbo.FROM TableA
    
    WHILE @LeftBoundary < @RowsToLoad
    BEGIN
        INSERT INTO TableB (Column1, Column2)
        SELECT
            tA.Column1,
            tB.Column2
        FROM
            dbo.TableA as tA
        WHERE
            tA.IdentifierColumn > @LeftBoundary
            AND tA.IdentifierColumn <= @RightBoundary
    
        SET @LeftBoundary = @LeftBoundary + @RowsPerBatch;
        SET @RightBoundary = @RightBoundary + @RowsPerBatch;
    END
    
    Run Code Online (Sandbox Code Playgroud)

    为了使其有效地工作,您真的要考虑dbo.TableA (IdentifierColumn)在运行负载时为其创建索引。