SQL Server 2012では800ミリ秒で実行され、SQL Server 2014では約170秒かかるクエリがあります。これを、Row Count Spool
演算子のカーディナリティーの見積もりが悪いものに絞り込んだと思います。スプールオペレーターについて少し読んだことがありますが(例:こことここ)、まだいくつかのことを理解できません。
- このクエリに
Row Count Spool
演算子が必要なのはなぜですか?正確さのために必要だとは思わないので、具体的にどのような最適化を提供しようとしているのですか? - SQL Serverが
Row Count Spool
演算子への結合がすべての行を削除すると推定するのはなぜですか? - これはSQL Server 2014のバグですか?もしそうなら、私はConnectにファイルします。しかし、私は最初により深い理解をお願いします。
注:LEFT JOIN
SQL Server 2012とSQL Server 2014の両方で許容可能なパフォーマンスを実現するために、クエリをとして書き換えるか、テーブルにインデックスを追加できます。この質問は、この特定のクエリを理解し、詳細に計画することに関するもので、詳細については説明しません。クエリの言い方を変えるには
遅いクエリ
完全なテストスクリプトについては、このPastebinを参照してください。これが私が見ている特定のテストクエリです:
-- Prune any existing customers from the set of potential new customers
-- This query is much slower than expected in SQL Server 2014
SELECT *
FROM #potentialNewCustomers -- 10K rows
WHERE cust_nbr NOT IN (
SELECT cust_nbr
FROM #existingCustomers -- 1MM rows
)
SQL Server 2014:推定クエリプラン
SQL Serverがあると考えているLeft Anti Semi Join
のは、Row Count Spool
1行に10,000行を絞り込むます。このため、LOOP JOIN
への後続の結合にa を選択し#existingCustomers
ます。
SQL Server 2014:実際のクエリプラン
期待どおり(SQL Server以外のすべての人が!)、Row Count Spool
行は削除されませんでした。したがって、SQL Serverが1回だけループすると予想される場合は、10,000回ループします。
SQL Server 2012:推定クエリプラン
SQL Server 2012(またはOPTION (QUERYTRACEON 9481)
SQL Server 2014)を使用する場合、Row Count Spool
は推定行数を削減せず、ハッシュ結合が選択されるため、はるかに優れた計画になります。
LEFT JOINの書き換え
参考までに、すべてのSQL Server 2012、2014、2016で良好なパフォーマンスを達成するためにクエリを書き直す方法を次に示します。ただし、上記のクエリの特定の動作と、新しいSQL Server 2014 Cardinality Estimatorのバグです。
-- Re-writing with LEFT JOIN yields much better performance in 2012/2014/2016
SELECT n.*
FROM #potentialNewCustomers n
LEFT JOIN (SELECT 1 AS test, cust_nbr FROM #existingCustomers) c
ON c.cust_nbr = n.cust_nbr
WHERE c.test IS NULL