このクエリは〜21秒で実行されます( 実行計画 ):
select
a.month
, count(*)
from SubqueryTest a
where a.year = (select max(b.year) from SubqueryTest b)
group by a.month
サブクエリが変数に置き換えられると、1秒未満で実行されます( 実行計画 )。
declare @year float
select @year = max(b.year) from SubqueryTest b
select
month
, count(*)
from SubqueryTest where year = @year group by month
実行計画から判断すると、「select max ...」副選択が「SubqueryTest a:」の数百万行ごとに実行されるため、非常に時間がかかります。
私の質問:サブセレクトはスカラーで確定的で相関がないため、クエリオプティマイザーが2番目の例で行った処理を実行してサブクエリを1回実行し、結果を格納してからメインクエリに使用しないのはなぜですか? SQL Serverの理解に問題があるだけだと思いますが、それを埋める手助けをしたいのですが、Googleでの数時間は役に立ちませんでした。
テーブルは1 GBをわずかに超え、約2800万のレコードがあります。
CREATE TABLE SubqueryTest(
[pk_id] [int] IDENTITY(1,1) NOT NULL
, [Year] [float] NULL
, [Month] [float] NULL PRIMARY KEY CLUSTERED ([pk_id] ASC))
CREATE NONCLUSTERED INDEX idxSubqueryTest ON SubqueryTest ([Year] ASC)
遅い計画ではない外部クエリの各行のMAX
を計算します。
実際、それを明示的に計算することは決してありません。
それはに似た計画を与えます
WITH CTE
AS (SELECT TOP(1) WITH TIES *
FROM SubqueryTest
WHERE year IS NOT NULL
ORDER BY year desc)
SELECT month,
count(*)
FROM CTE
GROUP BY month
スロープラン(推定行数)
year asc
に非カバーインデックスがあるため、それを逆方向にスキャンして、最初の年の行を取得します(暗黙のIS NOT NULL
述語のため、シークとして表示されます)。
残念ながら、行数を推定する場合、TOP 1
とTOP 1 WITH TIES
は区別されないようです。
この場合、それは大きな違いを生みます。 (推定2キールックアップ対実際の4,424,803)したがって、不適切な計画を取得します。
スロープラン(実際の行数)
month
をyear
のインデックスにキーまたはインクルード列として追加して、インデックスをカバーすることを検討できます。セカンダリキー列として追加することの利点は、追加の並べ替えなしでストリーム集約にフィードできることです(とにかく、ハッシュ集約は12個の異なる値で十分です)。
このような非選択的な列の非カバリングインデックスは、クエリの大部分では実際にはほとんど役に立ちません。インデックスは、テーブル全体で並列スキャンを実行し、27,445,400行すべての述語を評価する(膨大な数の検索を実行するよりも優先される)「高速」プランでは完全に無視されます。