【问题标题】:Data transfer insert into by chunk of 10,000 rows each time数据传输每次插入 10,000 行
【发布时间】:2014-11-12 23:57:08
【问题描述】:

我每天需要将 6M 行的表发送到另一台服务器。 当我通过以下方式进行测试时,它似乎永远不会完成:

insert into serverA.dbName.dbo.table1
select * from serverB.dbName.dbo.table1

有没有像“每次做 10000 行”这样的事情

类似

insert into serverA.dbName.dbo.table1
select * from serverB.dbName.dbo.table1
**by numOfRowEachTime=10000**

否则我会写一个循环。

【问题讨论】:

  • 我会写一个游标。或者这样做programmatically
  • 90% 的时间都使用光标是个坏主意。
  • 如果您的 SQL Server 许可允许您设置集成服务,您可以使用它。目的地可以简单地配置成块。
  • 每天复制超过 600 万行的最佳方法是使用 DTS 使用 BulkCopy 或编写一个使用 SQLBulkCopy 的小应用程序,批量大小为 100,000 以获得最佳性能。在您的情况下,它是一个长时间运行的过程,因为 SQL 服务器会记录每个插入操作。因此记录 6M 插入将花费大量时间。

标签: sql sql-server tsql bulkinsert data-transfer


【解决方案1】:

单个事务中的 600 万行一开始是一个相当大的块,跨链接服务器的 DML 语句也不是很好。但是要考虑的两种方法(对我有用)是:

  1. 如果有一种简单的方法可以按顺序复制它们(例如 ID 字段),那么您可以:

    • 跟踪在 ServerB 上的新表中发送的最后一个 ID
    • 在 ServerB 上编写一个执行类似于以下循环的 proc:

      DECLARE @StartingID INT = 1,
              @EndingID INT,
              @DataToTransport NVARCHAR(MAX);
      
      SELECT @StartingID = StartingID FROM dbo.ProcessStatus;
      
      WHILE (1 = 1)
      BEGIN
         ;WITH cte AS
         (
            SELECT TOP (10000) t1.ID,
                   ROW_NUMBER() OVER (ORDER BY t1.ID ASC) AS [RowNum]
            FROM   dbo.Table1 t1
            WHERE t1.ID >= @StartingID
         )
         SELECT TOP (1) @EndingID = cte.[ID] FROM cte
         ORDER BY cte.[RowNum] DESC;
      
         IF (@@ROWCOUNT = 0)
         BEGIN
            UPDATE dbo.ProcessStatus SET StartingID = 1;
            BREAK; -- reset to start at the beginning and exit
         END;
      
         SET @DataToTransport = (CONVERT(NVARCHAR(MAX), (
             SELECT t1.* FROM dbo.Table1 t1
             WHERE t1.ID BETWEEN @StartingID AND @EndingID
             FOR XML RAW));
      
         EXEC [ServerA].[dbName].[dbo].Table1_ImportFromServerB @DataToTransport;
      
         UPDATE dbo.ProcessStatus SET StartingID = @StartingID;
      
         SET @StartingID = (@EndingID + 1);
      END;
      
    • 在 ServerA -- [dbName].[dbo].Table1_ImportFromServerB -- 上编写一个 proc,它接受 NVARCHAR(MAX) 输入参数,将其转换为 XML,然后执行 INSERT INTO Table1 SELECT ... FROM (either @XMLvariable.Nodes or OPENXML)
    • 通过链接服务器将打包为 XML 的批处理发送到存储过程比通过链接服务器直接插入要快得多
    • 您必须将 XML 作为 NVARCHAR(MAX) 发送,因为不允许通过链接服务器发送 XML
    • [ProcessStatus] 表只是为了跟踪当前位置,以防进程失败,因此它可以从停止的地方开始,或者您甚至可以设置 X 次迭代的时间限制或计数器并计划从每 N 分钟左右执行一次 SQL 代理作业(我一直这样做)。


  2. 使用 SQLCLR 来利用为此类任务设计的 SqlBulkCopy 类。它本质上与 BCP、OPENROWSET(BULK...) 和 BULK INSERT 使用的 API 相同。您可以编写自己的基于 CLR 的存储过程,或者在 SQL# 库中已经完成并免费提供一个(我是该库的作者,但同样,它是免费的)。存储过程称为DB_BulkCopy,允许设置批量大小。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-08
    • 1970-01-01
    • 2017-03-18
    • 1970-01-01
    • 2012-10-21
    • 2017-10-12
    相关资源
    最近更新 更多