【问题标题】:SQL Server: Why isn't this logic working when Chunking on inserts?SQL Server:为什么在插入分块时这个逻辑不起作用?
【发布时间】:2016-08-25 20:37:59
【问题描述】:

技术人员--

我这里发生了一个无限循环的情况。为什么@@rowcount 永远不会被设置回0?我一定不明白@@rowcount 的真正作用——或者我将值设置在错误的位置。我认为该值应该在每次通过时递减,直到我最终达到零。

 DECLARE @ChunkSize int = 250000;

 WHILE @ChunkSize <> 0
 BEGIN
    BEGIN TRANSACTION

     INSERT TableName
      (col1,col2)
     SELECT TOP (@ChunkSize)
      col1,col2
     FROM TableName2

    COMMIT TRANSACTION;

    SET @ChunkSize = @@ROWCOUNT

   END -- transaction block

 END -- while-loop block

【问题讨论】:

  • scsimon,不,我想在@chunksize 减为零时跳出循环,只是这还没有成功! :)
  • select top(@ChunkSize) 的逻辑是什么?
  • 这就像说从 t2 中选择顶部 (250000) 行以插入到 t1。 @@rowcount 是我认为跟踪整个可能结果集的 mssql 引擎变量——但也许不是。
  • 好的,现在是 ee... 这更有意义。我马上编辑
  • Table2 中总会有记录,所以它永远不会结束,除非您跟踪该表中已经访问过的行,不是吗?

标签: sql sql-server chunking


【解决方案1】:

我不确定,根据您发布的内容,您将如何确保捕获尚未插入的行。如果你不这样做,那当然是一个无限循环。这是一种使用测试数据的方法——但您自然希望它基于 PK 或其他独特的列。也许你只是离开了那部分,或者我一起错过了一些东西。我只是对你的分块的最终代码及其背后的逻辑感兴趣,所以这是一个答案和询问。

if object_id('tempdb..#source') is not null drop table #source
if object_id('tempdb..#destination') is not null drop table #destination

create table #source(c1 int, c2 int)
create table #destination (c1 int, c2 int)

insert into #source (c1,c2) values
(1,1),
(2,1),
(3,1),
(4,1),
(5,1),
(6,1),
(7,1),
(8,1),
(9,1),
(10,1),
(11,1),
(12,1)


 DECLARE @ChunkSize int = 2;

 WHILE @ChunkSize <> 0
 BEGIN

        INSERT INTO #destination (c1,c2)
        SELECT TOP (@ChunkSize) c1,c2 FROM #source WHERE c1 NOT IN (SELECT DISTINCT c1 FROM #destination) ORDER BY ROW_NUMBER() OVER (ORDER BY C1)
        SET @ChunkSize = @@ROWCOUNT

    --SELECT @ChunkSize
 END 

 select * from #source
 select * from #destination

【讨论】:

    【解决方案2】:

    什么都没有发生,因为您在没有查看已插入的内容的情况下将 chunksize 设置为自身。使用您的示例,@Chunksize = 250000。首先,select 执行SELECT TOP 250000 并返回(大概)250000 行。然后您使用@@RowCount 更新@Chunksize,但返回的行数将为250000,因此您只需再次将其设置为250000。这可能没问题,除非在不排除您已经插入的行的情况下,这个数字永远不会改变 - 您将一遍又一遍地插入相同的 250000 行。

    你需要像NOT EXISTS 这样的东西来过滤掉你已经插入的行:

    DECLARE @ChunkSize int = 250000;
    
    WHILE @ChunkSize > 0
    BEGIN
      BEGIN TRANSACTION
    
       INSERT INTO TableName
        (col1,col2)
       SELECT TOP (@ChunkSize)
        col1,col2
       FROM TableName2 T2
       WHERE NOT EXISTS (SELECT *
                         FROM   TableName T
                         WHERE  T.Col1 = T2.Col1
                         AND    T.Col2 = T2.Col2)
    
      SET @ChunkSize = @@ROWCOUNT
      PRINT CONVERT(nvarchar(10),@ChunkSize) + ' Rows Inserted.';
    
      COMMIT TRANSACTION
    
    END -- while-loop block
    

    【讨论】:

    • 很高兴我不是唯一一个这样看的人
    • 更接近,没有更多的无限循环——但是一旦第一个 250000 被写入,这将是所有被写入的东西。我猜@@rowcount 不是我想的那样。看起来我需要 t2 的行数并从该行数递减,直到每个块完成。
    • @plditallo 你在下面测试了我的语法吗?对于我的示例,它可以按您预期的方式一次分块两行...在完成之前它将保持不变,除了最终选择可能小于原始块大小
    • @plditallo 哦,你是对的。那是因为交易设置。更新了我的答案。提交事务时会丢失行计数值。
    【解决方案3】:

    已实施的解决方案

    最后,我决定通过 SSIS 抽取 SQL,在那里我可以相应地设置提交批量大小。如果我没有选择帽子路线,我将不得不遵循@scsimon 的建议,并基本上维护一个跟踪表,用于记录已完成的记录和留待循环的记录。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多