IF EXISTS에서 쿼리를 래핑하면 속도가 매우 느려집니다.


16

아래 쿼리가 있습니다.

select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)

위 쿼리는 3 초 안에 완료됩니다.

위의 쿼리가 값을 반환하면 저장 프로 시저를 EXIT로 원하므로 아래처럼 다시 작성하십시오.

If Exists(
select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End

그러나 이것은 10 분이 걸립니다.

위와 같은 쿼리를 아래와 같이 다시 작성할 수 있으며 3 초 이내에 완료됩니다.

  select databasename 
from somedb.dbo.bigtable l where databasename ='someval' and source  <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End

위의 재 작성 문제는 위의 쿼리가 더 큰 저장 프로 시저의 일부이며 여러 결과 집합을 반환한다는 것입니다. C #에서는 각 결과 집합을 반복하고 일부 처리를 수행합니다.

위의 결과는 빈 결과 집합을 반환 하므로이 접근법을 사용하면 C #을 변경하고 다시 배포해야합니다.

제 질문은

왜 단지 IF EXISTS시간이 많이 걸리도록 계획을 바꾸는가?

다음은 도움이 될 수있는 세부 정보이며 세부 정보가 필요한 경우 알려주십시오.

  1. 테이블과 통계 스크립트를 작성하여 광산과 동일한 계획을 얻습니다.
  2. 느린 실행 계획
  3. 빠른 실행 계획

    Brentozar를 사용하여 느린 계획 붙여 넣기 계획 Brentozar를 사용하여 느린 계획
    붙여 넣기 계획

참고 : 두 쿼리는 동일하지만 (매개 변수 사용) 유일한 차이점은 EXISTS(익명화하는 동안 약간의 실수가 있었을 수도 있음)입니다.

테이블 생성 스크립트는 다음과 같습니다.

http://pastebin.com/CgSHeqXc- 작은 테이블 통계
http://pastebin.com/GUu9KfpS- 큰 테이블 통계


답변:


18

한 바와 같이 의해 설명 폴 화이트 : 자신의 블로그 게시물에 깊이에서 행 목표 : 안쪽 최적화EXISTS 선호하는 소개하는 행 목표, NESTED LOOPS또는 MERGE JOIN이상HASH MATCH

마지막 예제로 논리적 세미 조인 (예 : EXISTS로 도입 된 하위 쿼리)이 전체 테마를 공유한다고 가정하십시오. 첫 번째 일치하는 행을 빨리 찾도록 최적화되어야합니다.

쿼리에서 이것은 분명히 중첩 루프를 도입하고 병렬 처리를 제거하여 계획 속도가 느려집니다.

따라서 쿼리를 사용하지 않고 쿼리를 다시 작성할 수있는 방법을 찾아야 할 것입니다 NOT EXISTS.

a를 사용하여 쿼리를 다시 작성 LEFT OUTER JOIN하고 테스트를 통해 작은 테이블에 행이 없는지 확인하지 않아도됩니다.NULL

If EXISTS(
    SELECT databasename
    FROM somedb.dbo.bigtable l
    LEFT JOIN dbo.smalltable c ON c.source = l.source
    WHERE databasename = 'someval'
    AND source <> 'kt'
    AND c.source IS NULL
)

다음 EXCEPT과 같이 비교해야하는 필드 수에 따라 쿼리를 사용할 수도 있습니다 .

If EXISTS(
   SELECT source
   FROM somedb.dbo.bigtable l
   WHERE databasename = 'someval'
   AND source <> 'kt'

   EXCEPT

   SELECT source
   FROM dbo.smalltable
)

알았지, 아론 버트 랜드는 블로그 게시물이 그가하지 선호 EXISTS 이유를 제공하는 다른 접근 방식이 더 효과가 있는지를 파악할 수 있습니다 통해 읽어야하고, NULL 값의 경우 잠재적 인 정확성의 문제를 고려해야 할를.

관련 Q & A : 내장 된 select 문보다 오래 걸리는 경우


0

명시 적 조인을 사용하여 쿼리를 다시 작성하고 이와 같이 사용할 조인 작업 (루프, 해시 또는 병합)을 지정해야합니다.

If not exists(
    select databasename 
    from somedb.dbo.bigtable l
    inner hash join dbo.smalltable c 
        on c.source = l.source
where databasename ='someval' and source  <>'kt')
begin
    Raiserror('Source missing',16,1)
    Return
end

EXISTS 또는 NOT EXISTS를 사용하는 경우 조건을 만족시키기 위해 첫 번째 행을 찾아서 세트의 모든 행을 하나씩 통과해야한다고 가정하고 SQL Server에서 NESTED LOOP 연산을 사용하여 쿼리 계획을 생성했습니다. HASH JOIN을 사용하면 속도가 빨라집니다.


당신보다, 그것을 테스트합니다
TheGameiswar

0

같은 문제를 겪었습니다. "EXISTS"를 사용하지 않고 "COUNT ()"함수와 "IF ... ELSE"문을 사용하여 일을 처리했습니다.

예를 들어 다음을 시도하십시오.

IF
(
    SELECT
        COUNT(l.databasename) + 1 AS databasename
    FROM somedb.dbo.bigtable AS l

    WHERE   l.databasename ='someval'
        AND l.[source]  <> 'kt'
        AND NOT EXISTS(SELECT 1 FROM dbo.smalltable AS c WHERE c.[source]=l.[source])
) > 1 --Acts like EXISTS
BEGIN
    RAISERROR('Source missing', 16, 1)
RETURN
END

"+ 1"을 카운트에 추가하는 이유는 IF 조건에서 "> 1"을 사용할 수 있기 때문에 "> 0"또는 "<> 0"을 사용하면 쿼리가 HASH 대신 중첩 루프를 사용하도록 트리거됩니다. 시합. 왜 그런 일이 일어 났는지 정확히 알지 못했지만 왜 그런지 알아내는 것이 흥미로울 것입니다.

희망이 도움이됩니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.