하위 쿼리가 병렬 처리를 사용하고 조인이 사용되지 않는 이유는 무엇입니까?


16

하위 쿼리를 사용하지만 조인을 사용하지 않는이 쿼리를 실행할 때 SQL Server가 병렬 처리를 사용하는 이유는 무엇입니까? 결합 버전은 직렬로 실행되며 완료하는 데 약 30 배 더 걸립니다.

가입 버전 : ~ 30 초

여기에 이미지 설명을 입력하십시오

하위 쿼리 버전 : <1 초

여기에 이미지 설명을 입력하십시오

편집 : 쿼리 계획의 Xml 버전 :

가입 버전

SUBQUERY 버전

답변:


12

의견에 이미 표시된 것처럼 통계를 업데이트 해야하는 것처럼 보입니다.

location와 사이의 조인에서 나오는 예상 행 수testruns 두 계획 사이에 상당히 다릅니다.

가입 계획 견적 : 1

계획 1

하위 쿼리 계획 추정치 : 8,748

여기에 이미지 설명을 입력하십시오

조인에서 나오는 실제 행 수는 14,276입니다.

물론 조인 버전이 3 개의 행을 가져 와서 location하나의 조인 된 행을 생성 해야한다고 추정하는 반면, 하위 쿼리는 해당 행 중 하나가 동일한 조인에서 8,748을 생성하지만 그럼에도 불구하고 내가 할 수 있다고 추정합니다 이것을 재현합니다.

통계가 생성 될 때 히스토그램간에 교차가없는 경우에 발생합니다. 결합 버전은 단일 행을 가정합니다. 하위 쿼리의 단일 동등 탐색은 알 수없는 변수에 대한 동등 탐색과 동일한 추정 행을 가정합니다.

testruns의 카디널리티는입니다 26244. 세 개의 고유 한 위치 ID로 채워져 있다고 가정하면 다음 쿼리는 8,748행이 반환 될 것으로 추정합니다 ( 26244/3).

declare @i int

SELECT *
FROM   testruns AS tr
WHERE  tr.location_id = @i

테이블 locations에 3 개의 행만 포함되어 있으면 통계가 생성되는 상황을 파악하기가 쉽고 외래 키가 없다고 가정하면 실제 반환되는 행 수에 큰 영향을 주지만 데이터가 충분하지 않은 방식으로 데이터가 변경됩니다. 통계 자동 업데이트를 트립하고 임계 값을 다시 컴파일하십시오.

SQL Server가 해당 조인에서 나오는 행 수를 가져 오므로 조인 계획의 다른 모든 행 추정값이 크게 과소 평가됩니다. 직렬 계획을 얻는다는 의미뿐만 아니라 쿼리에 메모리 부여가 충분하지 않고 정렬 및 해시 조인이 유출됩니다 tempdb.

계획에 표시된 실제 행과 예상 행을 재현하는 가능한 시나리오는 다음과 같습니다.

CREATE TABLE location
  (
     id       INT CONSTRAINT locationpk PRIMARY KEY,
     location VARCHAR(MAX) /*From the separate filter think you are using max?*/
  )

/*Temporary ids these will be updated later*/
INSERT INTO location
VALUES      (101, 'Coventry'),
            (102, 'Nottingham'),
            (103, 'Derby')

CREATE TABLE testruns
  (
     location_id INT
  )

CREATE CLUSTERED INDEX IX ON testruns(location_id)

/*Add in 26244 rows of data split over three ids*/
INSERT INTO testruns
SELECT TOP (5984) 1
FROM   master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (5984) 2
FROM   master..spt_values v1, master..spt_values v2
UNION ALL
SELECT TOP (14276) 3
FROM   master..spt_values v1, master..spt_values v2

/*Create statistics. The location_id histograms don't intersect at all*/
UPDATE STATISTICS location(locationpk) WITH FULLSCAN;    
UPDATE STATISTICS testruns(IX) WITH FULLSCAN;

/* UPDATE location.id. Three row update is below recompile threshold*/
UPDATE location
SET    id = id - 100

그런 다음 다음 쿼리를 실행하면 추정치와 실제 불일치가 동일합니다.

SELECT *
FROM   testruns AS tr
WHERE  tr.location_id = (SELECT id
                         FROM   location
                         WHERE  location = 'Derby')

SELECT *
FROM   testruns AS tr
       JOIN location loc
         ON tr.location_id = loc.id
WHERE  loc.location = ( 'Derby' ) 

위치에 고유 제한 조건이 추가되면 "="가 정확히 하나의 행을 리턴한다는 것이 명백해집니다. 그런 다음 예제에서 쿼리 계획이 동일 해집니다 (스캔-> 탐색). alter table Location add constraint U_Location_Location unique nonclustered (Location);
crokusek

@crokusek 예. 당신이 나중에 무슨 뜻인지 깨달았고 내 의견을 삭제했습니다! 그러면 조인 버전의 예상 행 수가 하위 쿼리와 동일하게 증가합니까? PC에서 테스트 할 순간이 아닙니까?
Martin Smith

@crokusek p. 해당 단일 사례의 하위 쿼리와 동일한 조인의 예상 행과 같습니다.
Martin Smith

예. 동일한 쿼리 계획, 두 추정치 8748, 두 실제치 14276. Btw, locationId를 사전 계산하면 그 차이를 해결할 수 있다고 생각했지만 그렇지 않습니다.
crokusek

1
@crokusek-또한 DB 내 위치 및 기타 유사한 장소에 고유 제약 조건을 추가합니다. 쿼리 최적화에 영향을 미친다는 사실을 인정해야합니다. 데이터 무결성을 보장하는 것이라고 생각했습니다. 이 질문에 대한 의견을 보내 주셔서 감사합니다.
Chris L
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.