하위 쿼리를 사용하지만 조인을 사용하지 않는이 쿼리를 실행할 때 SQL Server가 병렬 처리를 사용하는 이유는 무엇입니까? 결합 버전은 직렬로 실행되며 완료하는 데 약 30 배 더 걸립니다.
가입 버전 : ~ 30 초
하위 쿼리 버전 : <1 초
편집 : 쿼리 계획의 Xml 버전 :
하위 쿼리를 사용하지만 조인을 사용하지 않는이 쿼리를 실행할 때 SQL Server가 병렬 처리를 사용하는 이유는 무엇입니까? 결합 버전은 직렬로 실행되며 완료하는 데 약 30 배 더 걸립니다.
가입 버전 : ~ 30 초
하위 쿼리 버전 : <1 초
편집 : 쿼리 계획의 Xml 버전 :
답변:
의견에 이미 표시된 것처럼 통계를 업데이트 해야하는 것처럼 보입니다.
location
와 사이의 조인에서 나오는 예상 행 수testruns
두 계획 사이에 상당히 다릅니다.
가입 계획 견적 : 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' )