このクエリを確認してください。非常にシンプルです(テーブルとインデックスの定義、および再現スクリプトについては、投稿の最後を参照してください)。
SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1 AND 1 = (SELECT 1);
注:「AND 1 =(SELECT 1)は、このクエリが自動パラメーター化されないようにするためのものです。問題を混乱させたように思いました。実際には、その句の有無にかかわらず、同じプランが取得されます。
そして、これが計画です( 計画リンクを貼り付け) :
そこに「トップ1」があるので、ストリーム集約演算子を見てびっくりしました。行は1つだけであることが保証されているため、私には必要ないようです。
その理論をテストするために、私はこの論理的に同等のクエリを試してみました。
SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1
GROUP BY Id;
これがその計画です( 計画リンクを貼り付け ):
案の定、group by planは、ストリーム集約演算子なしで問題を解決できます。
どちらのクエリもインデックスの末尾から「後方」を読み取り、「上位1」を実行して最大リビジョンを取得することに注意してください。
ここで何が欠けていますか? ストリーム集約は最初のクエリで実際に機能していますか、それとも排除することができますか(そしてオプティマイザの制限ではありません)?
ちなみに、これは信じられないほど実用的な問題ではないことに気づきました(どちらのクエリもCPUの0ミリ秒と経過時間を報告します)。ここに示されている内部/動作に興味があるだけです。
上記の2つのクエリを実行する前に実行したセットアップコードは次のとおりです。
DROP TABLE IF EXISTS dbo.TheOneders;
GO
CREATE TABLE dbo.TheOneders
(
Id INT NOT NULL,
Revision SMALLINT NOT NULL,
Something NVARCHAR(23),
CONSTRAINT PK_TheOneders PRIMARY KEY NONCLUSTERED (Id, Revision)
);
GO
INSERT INTO dbo.TheOneders
(Id, Revision, Something)
SELECT DISTINCT TOP 1000
1, m.message_id, 'Do...'
FROM sys.messages m
ORDER BY m.message_id
OPTION (MAXDOP 1);
INSERT INTO dbo.TheOneders
(Id, Revision, Something)
SELECT DISTINCT TOP 100
2, m.message_id, 'Do that thing you do...'
FROM sys.messages m
ORDER BY m.message_id
OPTION (MAXDOP 1);
GO
WHERE
句に一致する行がない場合は、この集計の役割を確認できます。
SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1
AND 1 = 1 /*To avoid auto parameterisation*/
AND Id%3 = 4 /*always false*/
その場合、ゼロ行は集約に入りますが、この場合、正しいセマンティクスはNULL
を返すため、1行を出力します。
これは、ベクトルとは対照的にスカラー集約です。
「論理的に同等」のクエリは同等ではありません。 GROUP BY Id
を追加すると、ベクトル集約になり、正しい動作は行を返さないことになります。
これについての詳細は スカラーとベクトルの集合体で楽しい を参照してください。