「SQL最終サブツリーコスト」と「クエリ時間パフォーマンス」の一般的な関係は何ですか?
例:クエリを調整し、サブツリーのコストが0.2から0.1になると、クエリ時間は2倍速くなりますか?私のクエリでこれが発生することはありません。
サーバーがあり、 "Set statistics time on"および "DBCC DROPCLEANBUFFERS"を使用しても、クエリのパフォーマンスを実際に測定することはできません。サーバー、トランザクション、プログラム、バックグラウンドアイテムで実行されているさまざまなプロセスがあります。
おかげで、
サブツリーコストは、計画の推定コストを表します。クエリオプティマイザがプランを選択した理由を調査するときに役立ちます。たとえば、ハッシュ結合を使用した計画を見て、ループ結合がより効率的な選択であったと考えるかもしれません。クエリヒントを追加してループ結合を強制し、サブツリーのコストを比較すると、SQL Serverがハッシュ結合を選択した理由を判断するのに役立ちます。
計画の推定コストは、ハードウェアの違い、他のプロセスによるブロック、サーバー全体のワークロード、モデルの制限、不完全な情報に基づく仮定など、多くの理由でクエリの「パフォーマンス時間」と一致しないことがよくあります。さらに、サブツリーのコストが0.1対0.2であっても、意味のある違いはまったくありません。残りのワークロードに対して相対的なコストが低いクエリがあり、そのクエリが長時間実行される場合、クエリオプティマイザーが誤った仮定または推論を行っていることを示しています。これらのタイプの問題の根本的な原因は、多くの場合、基数の見積もりに帰着します。一方、比較的高価なクエリが長時間実行されることがあります。時間がかかると推定される計画の部分を見ると、クエリが長時間実行されている理由に関する有用な手がかりが得られます。ただし、一部のクエリチューナーは、推定コストをまったく見ないように指示します。
以下は、見積もりコストと実行時間の間に極端な違いがある可能性があることを示すためのクエリの例です。 SQL Server 2017でテストしていますが、すべてのバージョンで同様のデモを考え出すことができます。まず、100kの連続した整数をヒープに入れます。
CREATE TABLE dbo.OptimizerUnits (ID BIGINT NOT NULL);
INSERT INTO dbo.OptimizerUnits WITH (TABLOCK)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);
次のクエリについて考えてみます。
SELECT ID
FROM dbo.OptimizerUnits
WHERE
(ID % 10) % 101 = 10
人間はそのクエリを見て、行を返さないと推測できますが、オプティマイザには現在、そのようなロジックが組み込まれていません。代わりに、約990行が返されると推測します。これにより、次のクエリの推定総コストは79590.1ユニットになります。
WITH OptimizerUnitsCTE (ID) AS
(
SELECT ID
FROM dbo.OptimizerUnits
WHERE
(ID % 10) % 101 = 10
)
SELECT TOP (100) t1.ID, t2.ID, t3.ID
FROM OptimizerUnitsCTE t1
CROSS JOIN OptimizerUnitsCTE t2
CROSS JOIN OptimizerUnitsCTE t3
ORDER BY t1.ID + t2.ID + t3.ID DESC;
ただし、クエリは私のマシンで50ミリ秒未満で実行されます。
それでは、別の方向に進みましょう。次のクエリについて考えてみます。
SELECT ID
FROM dbo.OptimizerUnits
WHERE
(ID % 10) % 101 = 1
AND (ID % 10) % 102 = 1
AND (ID % 10) % 103 = 1
AND (ID % 10) % 104 = 1
この場合も、人間は上記のクエリが正確に10000行を返すと推測できます。クエリオプティマイザーはそのことを認識していないため、クエリが16.7439行しか返さないと推測します。この結果、次のクエリの推定コストは1.45306オプティマイザユニットになります。
WITH OptimizerUnitsCTE (ID) AS
(
SELECT ID
FROM dbo.OptimizerUnits
WHERE
(ID % 10) % 101 = 1
AND (ID % 10) % 102 = 1
AND (ID % 10) % 103 = 1
AND (ID % 10) % 104 = 1
)
SELECT TOP (100) t1.ID, t2.ID, t3.ID
FROM OptimizerUnitsCTE t1
CROSS JOIN OptimizerUnitsCTE t2
CROSS JOIN OptimizerUnitsCTE t3
ORDER BY t1.ID + t2.ID + t3.ID DESC;
私のマシンでしばらくクエリを実行しましたが、完了するまでに約4.5日かかると推定しています。
要約すると、カーディナリティの推定値が低いと、79590.1ユニットのコストのクエリは1秒未満で完了し、1.45306ユニットのコストのクエリは約4.5日かかります。