この投稿は、行の目標に関する一連の記事の一部です。他の部分はここにあります:
- パート1:行の目標の設定と特定
- パート2:セミジョイン
このパートでは、オプティマイザーがアンチジョインの行目標を導入する時期と理由について説明します。
はじめに
アンチジョインは、アンチセミジョインとも呼ばれます。 一致しない結合入力Aから各行を返します 入力Bにあります。
反参加の場合:
- オプティマイザは可能性があります 適用に内側の行の目標を追加します (相関するネストされたループが結合します)アンチ結合のみ 。
- 行の目標は追加されません 相関のないネストされたループの場合は、アンチジョイン、ハッシュアンチジョイン、またはマージアンチジョイン。
- いつものように、行の目標は追加されるだけです 行の目標が適用されていない場合の見積もりよりも低い場合。
- 冗長な内側の
TOP
句とDISTINCT/GROUP BY
操作を簡素化できます。
上記の最初の箇条書きを拡張すると、セミジョインの適用とアンチジョインの行の目標の適用の主な違いは次のとおりです。
- セミジョインを適用すると、常に行の目標が設定されます (目標なしの見積もりよりも少ない限り)。
- アンチジョインを適用すると、行の目標が設定される場合があります 、ただし、論理的な反結合がコストベースの最適化中に適用に変換される場合のみ 。
これらのルールが単純ではないことをお詫びしますが、作成しませんでした。うまくいけば、いくつかの議論と例がそれをすべて明確にするでしょう。
デフォルトではアンチジョイン行の目標はありません
オプティマイザーは、人々が半参加を書くことを前提としています (間接的に、たとえばEXISTS
を使用する )検索されている行が見つかることを期待して 。セミジョインの適用行の目標が設定されています オプティマイザーによって、予想される一致する行をすばやく見つけるのに役立ちます。
反参加の場合 (例:NOT EXISTS
を使用して表現 )オプティマイザの仮定は、一致する行が見つからないということです。 。アンチジョインの適用行の目標が設定されていません オプティマイザによって、一致するものがないことを確認するためにすべての行をチェックする必要があると想定しているためです。
一致する行があることが判明した場合、行ゴールが使用された場合よりも、適用アンチ結合がこの行を見つけるのに時間がかかる可能性があります。それでも、(予期しない)一致が検出されるとすぐに、アンチジョインは検索を終了します。
アンチジョイン行の目標条件を適用する
SQL Serverは、アンチ結合を直接記述する方法を提供していないため、NOT EXISTS
などの回避策を使用する必要があります。 、NOT IN/ANY/SOME
、またはEXCEPT
。これらの各フォームは、クエリのコンパイルの開始時に、解析されたツリーにサブクエリ表現をもたらします。このサブクエリは常に適用に展開され、可能な場合は論理的な反結合に変換されます。 (詳細は、パート2で説明したセミジョインの場合と同じです)。これはすべて、些細な計画でさえ考慮される前に起こります。
アンチジョインが行ゴールを獲得するには、入力する必要があります 論理的アンチジョインとしてのコストベースの最適化 (上記の適用からの変換が成功している必要があることを意味します)。次に、コストベースのオプティマイザは、論理アンチジョインを適用として実装することを選択する必要があります。 。これを実現するには、オプティマイザは最初に探索を選択する必要があります 適用オプション。次に、選択する必要があります それが最も安価なオプションです(計画のその部分について)。
反結合行の目標は、結合を適用に変換できるコストベースの最適化ルールのいずれかによって設定されます。 入るアンチジョイン コストベースの最適化適用として (論理アンチジョインへの変換が失敗したため)されません 行の目標を適用します。
コストベースのオプティマイザは、一致する内側の行を見つける効率的な方法がある場合にのみ、[適用に参加]オプションを調べて選択します(たとえば、インデックスを使用します)。結合の内側のサブツリーに、適用述語が「ラッチオン」するのに役立つものがない場合、オプティマイザーはオプションを探索しません。これは、インデックス、一時インデックス(熱心なインデックススプールを介して)、またはその他の論理キーである可能性があります。行の目標を追加すると、最大1つの行を見つける必要がある場合に、オプティマイザーが結合して適用するオプションのコストを評価するのに役立ちます。
適用アンチ結合は、行ゴールのない実行プランに表示される場合があることに注意してください。これは、比較的一般的であるように、適用から結合への最初の変換が失敗した場合に発生します。これが発生すると、アンチジョインはコストベースのオプティマイザで適用として開始されるため、アプライへの結合ルールの1つによって行ゴールが追加されることはありません。
もちろん、このアプライの内側に、別のトップオペレーターなど、別のメカニズム(アプライに関連付けられていない)を介して行ゴールを導入することもできます。
要約すると:
- アンチジョインは、コストベースの最適化(CBO)中にのみ行の目標を達成できます。
- アンチジョインをアプライに変換するルールは、行の目標を追加します。
- アンチジョインは、アプライではなく、ジョインとしてCBOに入力する必要があります。
- CBOを結合として入力するには、初期のフェーズでサブクエリを結合として(適用ステージを介して)書き換えることができる必要があります。
- CBOは、有望な場合に変換を適用するための結合のみを調査します。
例
半結合を適用する場合よりも、反結合を適用するためにこれらすべてを示すのは少し難しいです。この理由については、パート4で説明します。
一方、これは、セミジョインの場合と同じ文書化されていないトレースフラグを使用して、行ゴールを使用したアンチジョインの適用がどのように発生するかを示すAdventureWorksの例です。コストベースの最適化の開始時の初期メモ構造を示すために、トレースフラグ8608が追加されました。
SELECT P.ProductID FROM Production.Product AS P WHERE NOT EXISTS ( SELECT 1 FROM Production.TransactionHistoryArchive AS THA WHERE THA.ProductID = P.ProductID UNION ALL SELECT 1 FROM Production.TransactionHistory AS TH WHERE TH.ProductID = P.ProductID ) OPTION (QUERYTRACEON 3604, QUERYTRACEON 8607, QUERYTRACEON 8608, QUERYTRACEON 8612, QUERYTRACEON 8621);
既存のサブクエリは最初に適用に変換されます:
<!-a href ="http://www.sqldat.com/article/uploadfiles/202205/2022051415254252.png" title="適用するサブクエリ"rel=lightbox target =_blank>
その後、適用は結合として正常に書き換えられます(この場合):
<!-a href ="http://www.sqldat.com/article/uploadfiles/202205/2022051415254247.png" title="参加に申し込む"rel=lightbox target =_blank>
最初のメモは、コストベースのオプティマイザに提示される論理的なアンチジョインを示しています(簡潔にするためにルートグループのみが示されています):
<!-a href ="http://www.sqldat.com/article/uploadfiles/202205/2022051415254354.png" title="初期メモルートグループ"rel=lightbox target =_blank>
オプティマイザの出力ツリーは次のとおりです。
<!-a href ="http://www.sqldat.com/article/uploadfiles/202205/2022051415254486.png" title="行ゴールとのアンチ結合のトレースフラグ出力"rel=lightbox target =_blank>
入力論理アンチジョインがアプライアンチジョインとして実装されていることに注意してください。連結演算子には1の行目標が設定されており、2つのテーブルアクセスのそれぞれにアクセスします。
推定実行プランは、外側の行ごとにアンチ結合の内側で1つの行が推定された適用アンチ結合を示しています。
SQL Server 2017CU3およびSSMS17.5を使用している場合、上記の連結およびインデックスシーク演算子のそれぞれの[プロパティ]ウィンドウに、 EstimateRowsWithoutRowGoalが表示されます。 トレースフラグ出力に表示されるカーディナリティ値に対応する値。この属性はツールチップには表示されず、行ゴールのない演算子では単に省略されます。個人的には、行の目標の影響を受ける計画の領域に微妙な陰影を付けると便利だと思います。そのため、各演算子を個別にクリックする必要はありません。
注意深い読者は、オプティマイザの出力ツリーに、最初にアクセスされているアーカイブテーブルが表示され、次に履歴テーブルが表示されていることに気付いたかもしれません。グラフィカルな計画では、その逆があります。最適化後の書き換えでは、予想されるコストに基づいて、連結演算子への入力を並べ替えることを検討します。この場合、履歴テーブルへのアクセスはアーカイブテーブルへのアクセス(0.085441ユニット)よりも安価(0.0850383ユニット)であると予想されるため、テーブルは最終計画で並べ替えられます。この最適化は、単一行の行の目標が存在する場合にのみ行われます。 UNIONALL最適化で説明したように。また、SQL Server 2008 R2以前、パッチが適用されたバージョンのSQL Server 2014および2016、トレースフラグ4199が有効になっている、またはSQLServer2017を実行している必要があります。
概要
セミジョインの適用には、常に内側に行ゴールがあります。オプティマイザーは、一致する行が存在することを前提としているため、その行をすばやく見つけるために行の目標を設定する価値があります。行の目標は、ループの反復ごとに最大1つの行が検出されるという事実も反映しています。
アンチジョインを適用する場合、デフォルトでは行の目標は設定されていません。オプティマイザのアンチジョインの前提は、一致する行はないということです。 が見つかるので、行の目標を設定することは逆効果になります。すべての行を消費する必要がある計画の一部は、行の目標ロジックの恩恵を受けません。
それでも、コストベースの最適化中にアンチジョインを適用するための行目標が設定される場合があります。これは、オプティマイザが論理アンチジョインをアプライに書き換えることを検討している場合にのみ発生します。コストベースの最適化が開始される前に通常の(相関のない)アンチジョインとして書き換えることができない論理アンチジョイン(間接T-SQL構文を使用して記述)には、行の目標が設定されません。
論理アンチジョインをアプライアンチジョインに変換して一致させることができるルールの場合、ジョインの内側に一致する行を見つけるための合理的な方法があるように見える必要があります。言い換えると、結合述語をプッシュダウンすることには、いくつかの潜在的な利点があるはずです。
このシリーズの最後のパートでは、私がアンチパターンと見なすアンチジョイン実行プランの形状を詳しく見ていきます。