サブクエリが並列処理を使用し、結合が使用しないのはなぜですか?


16

サブクエリを使用するこのクエリを実行するときに、SQL Serverが並列処理を使用するのはなぜですか?結合バージョンはシリアルで実行され、完了するまでに約30倍時間がかかります。

参加バージョン:〜30秒

ここに画像の説明を入力してください

副照会バージョン:<1秒

ここに画像の説明を入力してください

編集: クエリプランのXmlバージョン:

JOINバージョン

サブクエリバージョン

回答:


12

コメントですでに示したように、統計を更新する必要があるかのように見えます。

出てくる行の推定数は、間の結合locationtestruns2つのプランの間で非常に異なっています。

結合プランの見積もり:1

計画1

サブクエリプランの見積もり:8,748

ここに画像の説明を入力してください

結合から出てくる実際の行数は14,276です。

もちろん、結合バージョンが3行からlocation1つの結合行を生成することを推定する必要があるのに対し、サブクエリはそれらの行の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' ) 

一意の制約が場所に追加されると、「=」が正確に1行を返すことが明らかになります。次に、例でクエリプランが同一になります(スキャン->シーク):テーブルの場所を変更します。制約を追加します。
crokusek 14年

@crokusekはい。その後、あなたの意味を理解し、私のコメントを削除しました!これにより、結合バージョンの推定行数もサブクエリと同じになりますか?現時点では、PCでテストしていませんか?
マーティンスミス14年

@crokusekうん。そのシングルトンの場合のサブクエリの場合と同様に、結合から推定される行と同じように見えます。
マーティンスミス14年

はい。同一のクエリプラン、両方とも8748、両方とも実際14276を推定します。
crokusek 14年

1
@crokusek-DB内の場所やその他の同様の場所にも一意の制約を追加します。クエリの最適化に影響することを認識していなかったことを認めなければなりません。データの整合性を確保するだけだと思いました。この質問についてご意見をお寄せいただきありがとうございます。
クリスL 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.