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

时间:2017-01-16 16:26:26

标签: sql-server sql-server-2014

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

TableA没有任何索引,但TableB具有非群集唯一索引。

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

因为它们都有相同的结构,所以我试过

INSERT INTO TableB 
    SELECT * 
    FROM TableA;

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

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

1 个答案:

答案 0 :(得分:3)

首先,在插入行之前禁用TableB上的索引。您可以使用T-SQL执行此操作:

ALTER INDEX IX_Index_Name ON dbo.TableB DISABLE;  

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

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

现在,有几种解决问题的方法:

  1. 您必须没有数据丢失的可能性:使用您拥有的INSERT INTO ... SELECT ... FROM ...语法,但首先将数据库切换到批量记录恢复模式(read before switching )。如果您已经使用Bulk-logged或Simple进行操作,将无济于事。
  2. 首先导出数据:您可以使用BCP实用程序导出/导入数据。它支持批量加载数据。详细了解如何使用BCP实用程序here
  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
    

    为了有效地工作,你真的想考虑创建一个 仅在你的时间dbo.TableA (IdentifierColumn)上的索引 运行负载。