【问题标题】:Incremental batch delete and insert actions very slow in SQL ServerSQL Server 中的增量批量删除和插入操作非常慢
【发布时间】:2015-03-04 05:46:59
【问题描述】:

我有以下存储过程,它在表中删除和插入行。它运行缓慢。

我已经阅读了各种提案并已实施:

  • 批量删除行并使用派生表
  • 禁用 FK
  • where 子句中的字段的索引。

所以它应该走的路是:

  1. 表有大约 1000 万条记录。

  2. 每天我需要刷新大约 15-30% 的内容。我用这个SP来做。

来源:

CREATE PROCEDURE [dbo].[spIncrementalUpdate]
    -- Add the parameters for the stored procedure here
    @table_inc nvarchar(30),
    @table_target nvarchar(30),
    @table_date nvarchar(30),
    @field1 nvarchar(10),
    @field2 nvarchar(10)

AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
    DECLARE @logmessage as nvarchar(2048)
    DECLARE @logdatacode as int
    DECLARE @cmd as nvarchar(max)
    DECLARE @fromjulian nvarchar(10)
    DECLARE @datadate datetime
    DECLARE @datadate_str nvarchar(10)
    DECLARE @rows int
    DECLARE @ParmDefinition nvarchar(500)

    select @fromjulian = '114000'
    print 'Using ' + @fromjulian
    --
    -- GET THE ROW COUNT OF THE INC TABLE.
    --
    IF @field2 = ''
        BEGIN
            SET @cmd = N'SELECT @retval = count(*) from ' + 
                @table_inc + ' where ' + @field1 +' > ' + @fromjulian
        END
    ELSE
        BEGIN
            SET @cmd = N'SELECT @retval = count(*) from ' + 
                @table_inc + ' where ' + @field1 +' > ' + @fromjulian + 
                ' OR ' + @field2 +' > ' + @fromjulian
        END
    SET @ParmDefinition = N'@retval int OUTPUT'
    EXEC sp_executesql @cmd, @ParmDefinition, @retval = @rows OUTPUT
    --
    if @rows <> 0
    BEGIN
        SET @logmessage = @table_inc + ' has ' + 
            cast(@rows as nvarchar(10)) + 
            ' rows after ' + @fromjulian + ', deleting'
        SET @logdatacode = 1000
        --
        -- Delete the records from original table based on @fromjulian
        --
        IF @field2 = ''
            BEGIN
                SET @cmd = N'delete ' + @table_target + 
                    ' from (select top(50000) * from ' + @table_target  +
                     ' where ' + @field1 + ' > ' + @fromjulian + 
                     ' order by ' + @field1 + ') ' + 
                     @table_target
                print @cmd
            END
        ELSE
            BEGIN
                SET @cmd = N'delete ' + @table_target + 
                    ' from (select top(50000) * from ' + 
                    @table_target  + 
                    ' where ' + @field1 + ' > ' + @fromjulian +  
                    ' OR ' + @field2 +' > ' + @fromjulian + 
                    ' order by ' + @field1 + ',' + @field2 + ') ' + 
                    @table_target
                print @cmd
            END 
        SELECT 1
        WHILE @@ROWCOUNT <> 0
        BEGIN
            EXEC sp_executesql @cmd
        END
        --
        -- Inserting the records to target from INC table
        --
        IF @field2 = ''
            BEGIN
                SET @cmd = N'insert into ' + @table_target + 
                ' select * from ' + @table_inc + 
                ' where ' + @field1 +' > ' + cast(@fromjulian as nvarchar(10))
                print @cmd
            END
        ELSE
            BEGIN
                SET @cmd = N'insert into ' + @table_target + 
                ' select * from ' + @table_inc + 
                ' where ' + @field1 +' > ' + cast(@fromjulian as nvarchar(10)) + 
                ' OR ' + @field2 +' > ' + cast(@fromjulian as nvarchar(10))
                print @cmd
            END

        print @cmd
        EXEC sp_executesql @cmd
    END
    ELSE
        BEGIN
            SET @logmessage = 'NO ROWS IN ' + @table_inc + ' AFTER ' + cast(@fromjulian as nvarchar(10))
            SET @logdatacode = 1001
        END
    --
    -- LOG
    --
    INSERT INTO YLA_GROUP.[dbo].[sysssislog]
           ([event]
           ,[computer]
           ,[operator]
           ,[source]
           ,[sourceid]
           ,[executionid]
           ,[starttime]
           ,[endtime]
           ,[datacode]
           ,[databytes]
           ,[message])
     VALUES
           ('spIncrementalUpdate','','','',NEWID(),NEWID(),getdate(),getdate(),@logdatacode,null,@logmessage)
    print cast(@logdatacode as nvarchar(10)) + ' - ' + @logmessage
END

【问题讨论】:

    标签: sql-server performance tsql optimization delete-row


    【解决方案1】:

    由于您是批量删除行,因此您应该从子查询中删除 ORDER BY 子句,因为它不是必需的。

    以下是脚本的摘录:

    IF @field2 = ''
        BEGIN
            SET @cmd = N'delete ' + @table_target + 
                       ' from (select top(50000) * from ' + @table_target  +
                       ' where ' + @field1 + ' > ' + @fromjulian + ') ' +  @table_target
            print @cmd
        END
    ELSE
        BEGIN
            SET @cmd = N'delete ' + @table_target + 
                       ' from (select top(50000) * from ' + @table_target  + 
                       ' where ' + @field1 + ' > ' + @fromjulian +  
                       ' OR ' + @field2 +' > ' + @fromjulian + ') ' + @table_target
            print @cmd
        END 
    

    请记住,排序可能是一项昂贵的操作,如果删除(在这种情况下是正确的)索引,成本会更高。

    除此之外,您无能为力。还要考虑使用动态 SQL,执行计划不缓存。因此,每次您在@cmd 中执行命令时,查询引擎 都需要计算最佳执行计划。不幸的是,据我所知,您需要动态 SQL。

    【讨论】:

    • 我在某个地方(我现在找不到链接)看到他在派生表中使用 order by 来加快速度。请注意,在我订购的字段中启用了索引 ....
    • @e4rthdog 目前我无法弄清楚 ORDER BY 如何提高性能。我的意思是,让我们考虑一下,即使你有一个覆盖索引,它也是另一个操作。我认为没有 ORDER BY 值得一试。
    • @e4rthdog 好的。但我仍然持怀疑态度。您是否比较了有无order by的差异?你比较过执行计划吗?我很确定你会看到一个昂贵的排序运算符。
    • 我认为你是对的。没有 order by 似乎更快......虽然它仍然足够慢......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-11
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-15
    相关资源
    最近更新 更多