SQL Server 中的插入速度真的很慢

Posted

技术标签:

【中文标题】SQL Server 中的插入速度真的很慢【英文标题】:Insert rate really slow in SQL Server 【发布时间】:2017-04-28 21:32:15 【问题描述】:

在向表中插入 200 万行时,我需要帮助。我插入的表有 40 亿行,我插入的表有 200 万行。插入速率约为每分钟 190 行。

DECLARE @BatchSize INT = 5000

WHILE 1 = 1  
BEGIN 
   INSERT INTO [dbo].[a] ([a].[col1], [a].[col2], [a].[adate], [a].[importdate])
       SELECT TOP(@BatchSize) 
           b.col1,  
           b.col2,  
           b.adate,
           b.importdate
       FROM 
           b 
       WHERE 
           NOT EXISTS (SELECT 1 
                       FROM dbo.[a] 
                       WHERE [a].col1 = b.col1
                         AND [a].col2 = b.col2
                         AND [a].adate = b.adate)
                         --AND [sent].aDate > getdate()-10)

    IF @@ROWCOUNT < @BatchSize BREAK  
END;

在上面的查询中,在表a中,col1和col2和col3是主键(Non-clustered)。我想从表 b 中插入表 a 中的每条记录...

表 a 有 3 个索引,一个是 col1.col2,第二个是 col1,col2,col3,第三个是 col1 ......

任何人都可以提供任何关于使其更快的想法吗?

我在 SQL Server 2008 R2 上有 128 Gb RAM。

谢谢

【问题讨论】:

我想你想要的是 MERGE() 两百万行,我会一批插入。 桌上有触发器吗?如果主键是 col1+col2+col3 你不能插入 dups 所以你不需要 not exists 子句。继续选择要插入的前 5000 行。我不明白 M. Ali 的建议。这将创建一个非常大的交易。 一个包含大量行和插入(以及删除/更新)的非聚集表最终会产生“无数”前向指针(非常低效的索引),这可能是问题所在。 嗨@benjamin 我试过不存在,它给了我重复键无法插入的错误......有什么想法可以克服吗? 【参考方案1】:

由于您希望将B 中的所有行都插入A,因此不需要使用exists。问题变成跟踪先前批次中已经传输的行之一。以下示例生成一个行号并使用它来将行分组为批次。如果行号是按现有索引排序的,那么select 侧应该不需要排序传递。

-- Sample data.
declare @A as Table ( Col1 Int, Col2 Int );

declare @B as Table ( Col1 Int, Col2 Int );
insert into @B ( Col1, Col2 ) values
  ( 1, 1 ), ( 1, 2 ), ( 1, 3 ), ( 1, 4 ), ( 1, 5 ),
  ( 2, 1 ), ( 2, 2 ), ( 2, 3 ), ( 2, 4 ), ( 2, 5 );

-- Rows to transfer in each batch.
declare @BatchSize as Int = 5;
-- First row to transfer in the current batch.
declare @BatchMark as Int = 1;
-- Count of rows processed.
declare @RowsProcessed as Int = 1;

-- Process the batches.
while @RowsProcessed > 0
  begin
  insert into @A ( Col1, Col2 )
    select Col1, Col2
      from ( select Col1, Col2, Row_Number() over ( order by Col1, Col2 ) as RN from @B ) as PH
      where @BatchMark <= RN and RN < @BatchMark + @BatchSize;
  select @RowsProcessed = @@RowCount, @BatchMark += @BatchSize;
  select * from @A; -- Show progress.
  end;

替代方案包括向B 表添加一个标志列以标记已处理的行,使用B 表中的现有id 跟踪已处理的最大值,使用附加表跟踪索引值已处理的行,从B 中删除已处理的行,...。

output 子句可能对某些替代方案有用。

在传输数据之前使用合适的填充因子重建索引可能会有所帮助。见here。这取决于您的问题中没有的索引值的知识。

【讨论】:

嘿@habo 非常感谢您的回答,我试图获取索引的静态信息,但作为一个拥有 40 亿行的在线系统,每次我尝试检查 frag 之类的东西时索引。超时失败。还有一件事 。我可以删除 PK 并插入唯一记录并重新构建 PK .... 是个好选择吗? This 文章提供了一些有关重组和重建索引的附加信息。无论你做什么,都会有一个可观的价格。是否有一个开发数据库,​​其中包含您可以测试的最新数据副本?了解一下重建会造成的打击,以及它是否能让您更快地插入?确定合适的填充因子?

以上是关于SQL Server 中的插入速度真的很慢的主要内容,如果未能解决你的问题,请参考以下文章

sql server 2005 中的分区问题?

SQL Server - 有啥方法可以加快插入 TVF 中的表变量的速度?

SQL Server 存储过程在 SSMS 中运行速度很快,在应用程序中运行速度很慢 [重复]

sql server执行几十万条sql很慢,怎么办?

sql server 全文检索 使用

存储过程被程序和第三方客户端执行很慢,而sql server management studio执行速度正常