2016-07-21 4 views
1

업데이트해야 할 큰 테이블이 있습니다. 재귀 SQL 쿼리 최적화

CREATE TABLE T 
    ([Errors] varchar(4), [MRN] int, [EPI] varchar(13), [WD] varchar(4)); 

INSERT INTO T 
    ([Errors], [MRN], [EPI], [WD]) 
VALUES 
(NULL, 107, 'IP00001070001', 'AMUM'), 
(NULL, 107, 'IP00001070001', 'AMUM'), 
(NULL, 107, 'IP00001070001', 'KNAP'), 
(NULL, 107, 'IP00001070002', 'KNAP'), 
(NULL, 107, 'IP00001070002', 'KNAP'), 
(NULL, 107, 'IP00001070002', 'KNAP'), 
(NULL, 107, 'IP00001070002', 'KNAP'), 
(NULL, 381, 'IP00003810001', 'EAUS'), 
(NULL, 381, 'IP00003810001', 'EAUS'), 
(NULL, 381, 'IP00003810003', 'DOCK'), 
(NULL, 381, 'IP00003810003', NULL), 
(NULL, 45, 'IP00000450001', 'ASES'), 
('__', 45, 'IP00000450002', NULL), 
('__', 381, 'IP00003810002', NULL); 

내가의 WD의 값과 일치하는 경우는 null WD의 값이 레코드의 WD 열을 업데이트해야합니다 ... 다음의 예에 의해 정의 된 (그러나 광산은 1M + 행보다 열이 정말 방대하다)입니다 그들이 [MRN]과 [EPI]에 의해 명령 된 경우 첫 번째 항목. 예를 들어, 필요한 출력은 다음과 같습니다.

Errors MRN EPI    WD 
NULL 107 IP00001070001 AMUM 
NULL 107 IP00001070001 AMUM 
NULL 107 IP00001070001 KNAP 
NULL 107 IP00001070002 KNAP 
NULL 107 IP00001070002 KNAP 
NULL 107 IP00001070002 KNAP 
NULL 107 IP00001070002 KNAP 
NULL 381 IP00003810001 EAUS 
NULL 381 IP00003810001 EAUS 
NULL 381 IP00003810003 EAUS 
NULL 381 IP00003810003 EAUS 
NULL 45 IP00000450001 ASES 
__  381 IP00003810003 EAUS 
__  45 IP00000450002 ASES 
__  381 IP00003810002 EAUS 

편집 된 레코드가 맨 아래에 있습니다. 이것이 내가 원하는거야. 그러나이 방법은 SLLLLOOOOWWW ... 매우 느리고 좋은 이유 때문에 전체 세트를 반복합니다. 내 질문은 이미 목표 테이블을 인덱싱하고 있습니다.

  1. 이 쿼리/연산을 어떻게 최적화 할 수 있습니까?
  2. 여기에도 재귀가 필요합니까? 시간에 대한

    IF EXISTS (
        SELECT name 
        FROM sys.tables 
        WHERE name = N'T') 
    DROP TABLE [T] 
    GO 
    
    CREATE TABLE T 
        ([Errors] varchar(4), [MRN] int, [EPI] varchar(13), [WD] varchar(4)); 
    
    INSERT INTO T 
        ([Errors], [MRN], [EPI], [WD]) 
    VALUES 
        (NULL, 107, 'IP00001070001', 'AMUM'), 
        (NULL, 107, 'IP00001070001', 'AMUM'), 
        (NULL, 107, 'IP00001070001', 'KNAP'), 
        (NULL, 107, 'IP00001070002', 'KNAP'), 
        (NULL, 107, 'IP00001070002', 'KNAP'), 
        (NULL, 107, 'IP00001070002', 'KNAP'), 
        (NULL, 107, 'IP00001070002', 'KNAP'), 
        (NULL, 381, 'IP00003810001', 'EAUS'), 
        (NULL, 381, 'IP00003810001', 'EAUS'), 
        (NULL, 381, 'IP00003810003', 'DOCK'), 
        (NULL, 381, 'IP00003810003', 'DOCK'), 
        (NULL, 45, 'IP00000450001', 'ASES'), 
        ('__', 381, 'IP00003810003', NULL), 
        ('__', 45, 'IP00000450002', NULL), 
        ('__', 381, 'IP00003810002', NULL); 
    
    IF EXISTS (SELECT * 
          FROM sys.indexes 
          WHERE name='idxEETEST' AND object_id = OBJECT_ID('T')) 
    DROP INDEX [idxEETEST] ON [T]; 
    GO 
    
    CREATE NONCLUSTERED INDEX [idxEpiIPWardLoad] 
    ON [T] ([MRN], [EPI]) 
    GO 
    
    DECLARE @sql NVARCHAR(MAX) 
    DECLARE @mrn INT 
    DECLARE @epi NVARCHAR(16) 
    DECLARE @get_rec CURSOR 
    SET @get_rec = CURSOR FOR 
        SELECT MRN, EPI 
        FROM T 
        WHERE Errors IS NOT NULL 
    OPEN @get_rec 
    FETCH NEXT 
    FROM @get_rec INTO @mrn, @epi 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
        SET @sql = 
         'DECLARE @wd VARCHAR(4); ' + 
         'SELECT TOP 1 @wd = WD ' + 
         'FROM T ' + 
         'WHERE MRN = ' + Convert(VARCHAR, @mrn) + ';' + 
         'UPDATE T ' + 
         'SET WD = @wd ' + 
         'WHERE MRN = ' + Convert(VARCHAR, @mrn) + ' AND EPI = ''' + @epi + '''' 
        EXEC(@sql); 
    
        FETCH NEXT 
        FROM @get_rec INTO @mrn, @epi 
    END 
    CLOSE @get_rec 
    DEALLOCATE @get_rec 
    GO 
    
    IF EXISTS (SELECT * 
          FROM sys.indexes 
          WHERE name='idxEETEST' AND object_id = OBJECT_ID('T')) 
    DROP INDEX [idxEETEST] ON [T]; 
    GO 
    

    감사 :

여기에 도움을 기꺼이 누군가를 돕기 위해 설정 한 전체 테스트 쿼리입니다.

+0

동적 SQL과 함께 루프를 사용하여 업데이트하는 이유는 무엇입니까? 이것은 동적 SQL이나 루핑이 전혀없는 단일 업데이트 문으로 다시 작성 될 수 있습니다. –

+0

나는 지옥처럼 녹슬고 재귀없이 동일한 테이블을 업데이트하는 일괄 처리의 첫 번째 레코드를 얻는 방법을 알기 위해 고심하고 있었기 때문에 고심했다. 어떤 도움이라도 크게 받게 될 것입니다. – MoonKnight

+0

나는 당신이 여기에서 성취하려고하는 논리를 따르지 않는다. 비즈니스 규칙을 설명 할 수 있습니까? –

답변

0

I을 향상

각 필터링 전) TABLE_NAME ON INDEX의 INDEX_NAME을 CREATE 2 개 인덱스 (MRN)

II) TABLE_NAME 인덱스의 INDEX_NAME를 CREATE (MRN, EPI) 네가 무엇인지 이해한다고 생각해. 하려고하는 것. 원하는 출력을 원하는대로 출력하면 도움이 될 것입니다. 그러나 게시 된 규칙과 데이터로이 작업이 가능합니다.

update t1 set WD = u.NewWD 
FROM T t1 
cross apply 
(
    select top 1 WD as NewWD 
    from T t2 
    where t2.MRN = t1.MRN 
    order by t2.EPI 
)u 
where t1.Errors is not null 
+0

큰 테이블에는 성가 시게됩니다. – NEER

0

아직 귀하의 질문에 대해 명확하지 않습니다. 그러나 여전히 동일한 쿼리를 사용하려는 경우 다음과 같은 개선 사항을 고려할 수 있습니다.

1) 왜 varchar로 데이터 변환을 수행하고 있습니까? 변환이 2 곳에서 수행되고 제거하면 성능이 향상됩니다. (희망 변환은 필요 없음)

2) 색인 - 'MRN'및 'EPI'를 기준으로 필터링 할 때, 또한 성능

+1

그러나 색인 생성은 여기에서 문제가되지 않습니다. 실제 성능 싱크는 커서입니다. –