2015-01-06 1 views
2

테이블에 행을 삭제하고 삽입하는 다음 저장 프로 시저가 있습니다. 천천히 달리고 있어요.SQL Server에서 증분 배치 삭제 및 삽입 작업이 매우 느립니다.

나는 다양한 제안을 읽고 난 구현 :

  • 가 일괄 행을 삭제하고 where 절에있는 필드에 테이블을
  • 안 FK
  • 인덱스 파생 사용.

는 그래서 가야 방법은 다음과 같습니다

  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 

답변

2

당신이 일괄 행을 삭제하고 있기 때문에, 당신은 당신의 하위 쿼리에서 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이 필요합니다.

+0

나는 물건을 빠르게하기 위해 파생 된 테이블에서 주문을 사용한 어딘가 (지금은 링크를 찾을 수 없음)를 보았습니다. 주문하는 필드에 인덱스가 설정되어 있다는 것을 유의하십시오. – e4rthdog

+1

@ e4rthdog 순간에 ORDER BY가 성능을 어떻게 향상시킬 수 있는지 알 수 없습니다. 내 생각에, 그것에 대해 생각해 봅시다, 당신은 커버 인덱스가 있더라도 그것을 만드는 또 다른 작업입니다. ORDER BY없이 시도해 볼 가치가 있다고 생각합니다. – dario

+0

이것은 내가 말하고자하는 것입니다 : http://sqlblogcasts.com/blogs/simons/archive/2009/05/22/DELETE-TOP-x-rows-avoiding-a-table-scan.aspx – e4rthdog