SARGable 술어는 언제 CTE 또는 파생 테이블로 푸시 될 수 있습니까?


15

샌드백

최고 품질의 블로그 Posts®에서 작업하는 동안, 나는 정말 발견 된 일부 최적화 동작 건너 온 화나는 재미를. 나는 즉시 설명이 없습니다. 적어도 하나도 만족스럽지 않으므로 누군가 똑똑하게 나타날 수 있도록 여기에 넣겠습니다.

따라하고 싶다면 2013 버전의 스택 오버플로 데이터 덤프를 여기 에서 가져올 수 있습니다 . Comments 테이블과 하나의 추가 인덱스를 사용하고 있습니다.

CREATE INDEX [ix_ennui] ON [dbo].[Comments] ( [UserId], [Score] DESC );

쿼리 하나

이렇게 테이블을 쿼리하면 이상한 쿼리 계획이 나타납니다 .

WITH x
    AS
     (
         SELECT   TOP 101
                  c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
         ORDER BY c.Score DESC
     )
SELECT *
FROM   x
WHERE  x.Score >= 500;

견과류

Score의 SARGable 술어는 CTE 내부로 푸시되지 않습니다. 계획에서 훨씬 나중에 필터 연산자에 있습니다.

견과류

ORDER BY필터와 동일한 열에 있기 때문에 이상하다고 생각 합니다.

쿼리 2

쿼리를 변경하면 푸시됩니다.

WITH x
    AS
     (
         SELECT   c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
     )
SELECT TOP 101 *
FROM   x
WHERE  x.Score >= 500
ORDER BY x.Score DESC;

쿼리 계획 변경 도, 디스크없이 유출로, 훨씬 빠르게 실행됩니다. 비 클러스터형 인덱스 스캔의 조건 자와 함께 동일한 결과를 생성합니다.

견과류

견과류

쿼리 3

이것은 다음과 같이 쿼리를 작성하는 것과 같습니다.

SELECT   TOP 101
         c.UserId, c.Text, c.Score
FROM     dbo.Comments AS c
WHERE c.Score >= 500
ORDER BY c.Score DESC;

쿼리 4

파생 테이블을 사용하면 초기 CTE 쿼리와 동일한 "잘못된"쿼리 계획이 적용됩니다.

SELECT *
FROM   (   SELECT   TOP 101
                    c.UserId, c.Text, c.Score
           FROM     dbo.Comments AS c
           ORDER BY c.Score DESC ) AS x
WHERE x.Score >= 500;

다음과 같은 경우 상황이 더욱 악화됩니다.

데이터를 오름차순으로 정렬하도록 쿼리를 변경하고 필터를로 변경 <=합니다.

이 질문을 오랫동안하지 않기 위해 모든 것을 하나로 모을 것입니다.

쿼리

--Derived table
SELECT *
FROM   (   SELECT   TOP 101
                    c.UserId, c.Text, c.Score
           FROM     dbo.Comments AS c
           ORDER BY c.Score ASC ) AS x
WHERE x.Score <= 500;


--TOP inside CTE
WITH x
    AS
     (
         SELECT   TOP 101
                  c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
         ORDER BY c.Score ASC
     )
SELECT *
FROM   x
WHERE  x.Score <= 500;


--Written normally
SELECT   TOP 101
         c.UserId, c.Text, c.Score
FROM     dbo.Comments AS c
WHERE c.Score <= 500
ORDER BY c.Score ASC;

--TOP outside CTE
WITH x
    AS
     (
         SELECT   c.UserId, c.Text, c.Score
         FROM     dbo.Comments AS c
     )
SELECT TOP 101 *
FROM   x
WHERE  x.Score <= 500
ORDER BY x.Score ASC;

계획

링크 계획 .

견과류

이러한 쿼리 중 비 클러스터형 인덱스를 활용하는 쿼리는 없습니다. 여기서 변경되는 것은 필터 연산자의 위치뿐입니다. 술어가 인덱스 액세스로 푸시되는 경우는 없습니다.

질문이 나타납니다!

SARGable 술어가 다른 시나리오가 아닌 일부 시나리오에서 푸시 될 수있는 이유가 있습니까? 내림차순으로 정렬 된 쿼리 내의 차이점은 흥미롭지 만, 기괴한 오름차순의 차이점은 흥미 롭습니다.

관심있는 사람들을 위해 다음과 같은 인덱스 만있는 계획이 있습니다 Score.

답변:


11

몇 가지 문제가 있습니다.

술어 과거 밀어 넣기 TOP

옵티마이 저는 현재 TOP안전한 경우가 제한된 경우에도 술어를 a 이상으로 푸시 할 수 없습니다 . 이 제한 사항은 술어가 범위보다 높은 범위에있는 질문의 모든 쿼리 동작을 설명합니다 TOP.

해결 방법은 다시 쓰기를 수동으로 수행하는 것입니다. 근본적인 문제는 같은 특화된 규칙이없는 것을 제외하고, 술어를 창 함수를 지나치 는 경우와 유사합니다 SelOnSeqPrj.

제 개인적인 견해는 SelOnTop사람들이 TOP일종의 '최적화 담장'을 제공하기 위해 의도적으로 쿼리를 작성했기 때문에 같은 탐색 규칙 은 구현되지 않은 상태 입니다.

* 일반적으로 이것은 술어가 ORDER BY와 관련된 절에 나타나고 TOP부등식의 방향이 정렬 방향과 일치해야 함을 의미합니다. 또한 변환은 SQL Server에서 NULL의 정렬 동작을 설명해야합니다. 전반적으로, 한계는 아마도이 변형이 추가 탐사 노력을 정당화하기에 실제로는 충분히 유용하지 않다는 것을 의미합니다.

비용 문제

문제의 나머지 실행 계획 때문에의 값의 배포 비용 기반 선택지로서 설명 될 수 Score열 (더 많은 열 = 500 <보다 = 500>) 및 효과 행 목표 의해 도입 TOP.

예를 들면 다음과 같습니다.

--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score <= 500
ORDER BY
    c.Score ASC;

... 필터에서 명백하게 푸쉬되지 않은 술어를 가진 계획을 생성합니다.

행 목표로 인한 늦은 필터

정렬은 101 개의 행을 생성하는 것으로 추정됩니다. 이것은 Top에 의해 추가 된 행 목표의 효과입니다. 이는 정렬 및 필터의 예상 비용에 영향을 미치며이 옵션이 더 저렴한 옵션 인 것처럼 보일 수 있습니다. 이 계획의 예상 비용은 2401.39 단위입니다.

쿼리 힌트로 행 목표를 비활성화하는 경우 :

--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score <= 500
ORDER BY
    c.Score ASC
OPTION (USE HINT ('DISABLE_OPTIMIZER_ROWGOAL'));

... 생산 계획은 다음과 같습니다.

행 목표가없는 계획

술어는 비파괴 가능한 술어로서 스캔으로 푸시되었으며 전체 계획의 비용은 2402.32 단위입니다.

것을 알 <= 500술어가 행을 필터링 할 것으로 예상되지 않는다. 와 같이 더 작은 수를 선택한 경우 <= 50옵티마이 저는 행 목표 효과에 관계없이 푸시 조건 자 계획을 선호합니다.

with Score DESCScore >= 500술어에 대한 쿼리의 경우 :

--Written normally
SELECT TOP (101)
    c.UserId, 
    c.[Text],
    c.Score
FROM dbo.Comments AS c
WHERE
    c.Score >= 500
ORDER BY
    c.Score DESC;

이제 조건이 최적화이 선택하는 술어를 밀어 수 있도록, 매우 선택적 될 것으로 예상된다 조회와 클러스터되지 않은 인덱스를 사용 :

선택적 술어

다시 한 번, 옵티마이 저는 여러 대안을 고려하여 평상시처럼 가장 저렴한 옵션으로 선택했습니다.

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