サブクエリを使用するこのクエリを実行するときに、SQL Serverが並列処理を使用するのはなぜですか?結合バージョンはシリアルで実行され、完了するまでに約30倍時間がかかります。
参加バージョン:〜30秒
副照会バージョン:<1秒
編集: クエリプランのXmlバージョン:
サブクエリを使用するこのクエリを実行するときに、SQL Serverが並列処理を使用するのはなぜですか?結合バージョンはシリアルで実行され、完了するまでに約30倍時間がかかります。
参加バージョン:〜30秒
副照会バージョン:<1秒
編集: クエリプランのXmlバージョン:
回答:
コメントですでに示したように、統計を更新する必要があるかのように見えます。
出てくる行の推定数は、間の結合location
とtestruns
2つのプランの間で非常に異なっています。
結合プランの見積もり:1
サブクエリプランの見積もり:8,748
結合から出てくる実際の行数は14,276です。
もちろん、結合バージョンが3行からlocation
1つの結合行を生成することを推定する必要があるのに対し、サブクエリはそれらの行の1つが同じ結合から8,748を生成することを推定するという直感的な意味はまったくありませんが、これを再現します。
これは、統計が作成されるときにヒストグラム間にクロスオーバーがない場合に発生するようです。結合バージョンは単一の行を想定しています。また、サブクエリの単一の等価シークは、未知の変数に対する等価シークと同じ推定行を想定しています。
テストランのカーディナリティはです26244
。3つの異なるロケーションIDが入力されていると仮定すると、次のクエリは8,748
行が返されると推定します(26244/3
)
declare @i int
SELECT *
FROM testruns AS tr
WHERE tr.location_id = @i
テーブルにlocations
含まれる行が3行のみである場合、統計が作成され、実際に返される行の数に劇的に影響するような方法でデータが変更される状況を簡単に(外部キーがないと仮定した場合)統計の自動更新をトリップし、しきい値を再コンパイルします。
SQL Serverがその結合から出てくる行の数を非常に誤って取得するため、結合プランの他の行の見積もりはすべて非常に過小評価されています。シリアルプランを取得するという意味だけでなく、クエリは十分なメモリ許可を取得できず、並べ替えとハッシュ結合が流出しtempdb
ます。
計画に示されている実際の行と推定された行を再現する可能なシナリオの1つを以下に示します。
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' )