COALESCE
いくつかの列でそれらを結合することは良い習慣ではないことを知っています。
スキーマが3NF +(キーおよび制約付き)で、クエリがリレーショナルであり、主にSPJG(selection-projection-join-group by)である場合、適切なカーディナリティおよび分布推定値を生成するのは十分困難です。CEモデルは、これらの原則に基づいて構築されています。より多くの珍しいまたは非リレーショナルクエリである特徴を、より近い1はカーディナリティと選択のフレームワークが扱うことができるものの境界になります。行き過ぎて、CEはあきらめて推測しますます。
MCVEの例の大部分は単純なSPJ(Gなし)です。ただし、単純な内部の等価結合(または半結合)ではなく、主に外部の等価結合(内部結合と反半結合としてモデル化)があります。すべてのリレーションにはキーがありますが、外部キーやその他の制約はありません。結合の1つを除くすべてが1対多であり、これは良いことです。
例外はある多対多の外側の間に参加X_DETAIL_1
してX_DETAIL_LINK
。MCVEでのこの結合の唯一の機能は、で潜在的に行を複製することX_DETAIL_1
です。これは珍しいです種類のものです。
単純な等式述部(選択)とスカラー演算子も優れています。たとえば、属性比較と等しい属性/定数は、通常、モデルで適切に機能します。このような述語の適用を反映するために、ヒストグラムと頻度統計を修正するのは比較的「簡単」です。
COALESCE
は、に基づいて構築されますCASE
。これは、次のように内部的に実装されますIIF
(これはIIF
、Transact-SQL言語に登場するずっと前から真実でした)。CE IIF
は、UNION
相互に排他的な2つの子を持つモデルを作成し、それぞれが入力関係の選択に関するプロジェクトで構成されます。リストされている各コンポーネントにはモデルがサポートされているため、それらを組み合わせるのは比較的簡単です。それでも、抽象化が1層になると、最終結果の精度が低下する傾向があります。これは、より大きな実行計画の安定性と信頼性が低下する理由です。
ISNULL
、一方で、エンジンに固有のものです。これ以上基本的なコンポーネントを使用して構築されることはありません。ISNULL
たとえば、ヒストグラムへの効果の適用は、NULL
値のステップを置き換える(および必要に応じて圧縮する)のと同じくらい簡単です。スカラー演算子が行くように、それはまだ比較的不透明であり、可能な限り避けるのが最善です。それにも関わらず、一般的に言うと、CASE
ベースの代替よりもオプティマイザーにやさしい(オプティマイザーにやさしくない)。
CE(70および120+)は、SQL Server標準でも非常に複雑です。単純なロジック(秘密の式を使用)を各演算子に適用する場合ではありません。CEはキーと機能の依存関係を知っています。頻度、多変量統計、ヒストグラムを使用して推定する方法を知っています。そして、特別なケース、改良、チェックとバランス、およびサポート構造の絶対的なトンがあります。多くの場合、たとえば複数の方法で結合(頻度、ヒストグラム)を推定し、2つの違いに基づいて結果または調整を決定します。
最後に、基本的なことを1つ説明します。最初のカーディナリティの推定は、クエリツリー内のすべての操作に対して、ボトムアップで実行されます。選択性とカーディナリティーは、最初にリーフ演算子から派生します(基本関係)。修正されたヒストグラムと密度/頻度情報は、親演算子に対して導出されます。ツリーが上に行くほど、エラーが蓄積する傾向があるため、推定の品質が低下する傾向があります。
この単一の最初の包括的な推定は出発点を提供し、最終的な実行計画を考慮する前に発生します(些細な計画のコンパイル段階よりも前に行われます)。この時点でのクエリツリーは、クエリの記述形式をかなり密接に反映する傾向があります(ただし、サブクエリが削除され、簡略化が適用されるなど)。
SQL Serverは、初期推定の直後に、ヒューリスティックな結合の並べ替えを実行します。これは、大まかに言ってツリーを並べ替えて、より小さなテーブルと高選択性の結合を最初に配置しようとします。また、外部結合とクロス積の前に内部結合を配置しようとします。その機能は広範囲ではありません。その努力は完全ではありません。物理的なコストは考慮されません(まだ存在しないため、統計情報とメタデータ情報のみが存在するため)。ヒューリスティックな順序変更は、単純な内部等価結合ツリーで最も効果的です。コストベースの最適化の「より良い」出発点を提供するために存在します。
この結合カーディナリティの推定値が非常に大きいのはなぜですか?
MCVEには、「冗長な」主に冗長な多対多の結合がありCOALESCE
、述語には等結合があります。また、演算子ツリーには、内部結合lastがあります。これは、ヒューリスティック結合再順序付けにより、ツリーをより望ましい位置に移動できませんでした。すべてのスカラーと投影を除いて、結合ツリーは次のとおりです。
LogOp_Join [ Card=4.52803e+009 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_LeftOuterJoin [ Card=481577 ]
LogOp_Get TBL: X_DRIVING_TABLE(alias TBL: dt) [ Card=481577 ]
LogOp_Get TBL: X_DETAIL_1(alias TBL: d1) [ Card=70 ]
LogOp_Get TBL: X_DETAIL_LINK(alias TBL: lnk) [ Card=47 ]
LogOp_Get TBL: X_DETAIL_2(alias TBL: d2) X_DETAIL_2 [ Card=119 ]
LogOp_Get TBL: X_DETAIL_3(alias TBL: d3) X_DETAIL_3 [ Card=281 ]
LogOp_Get TBL: X_LAST_TABLE(alias TBL: lst) X_LAST_TABLE [ Card=94025 ]
不完全な最終推定値がすでに設定されていることに注意してください。これはCard=4.52803e+009
、倍精度浮動小数点値4.5280277425e + 9(10進数で4528027742.5)として印刷され、内部に格納されます。
元のクエリの派生テーブルが削除され、投影が正規化されました。最初のカーディナリティおよび選択性の推定が実行されたツリーのSQL表現は次のとおりです。
SELECT
PRIMARY_ID = COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID)
FROM X_DRIVING_TABLE dt
LEFT OUTER JOIN X_DETAIL_1 d1
ON dt.ID = d1.ID
LEFT OUTER JOIN X_DETAIL_LINK lnk
ON d1.LINK_ID = lnk.LINK_ID
LEFT OUTER JOIN X_DETAIL_2 d2
ON dt.ID = d2.ID
LEFT OUTER JOIN X_DETAIL_3 d3
ON dt.ID = d3.ID
INNER JOIN X_LAST_TABLE lst
ON lst.JOIN_ID = COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID)
(余談ですが、繰り返しCOALESCE
は最終計画にも存在します。1回は最終のCompute Scalarに、もう1回は内部結合の内側にあります)。
最終的な結合に注目してください。この内部結合は、(定義により)のデカルト積でX_LAST_TABLE
あり、選択(結合述語)がlst.JOIN_ID = COALESCE(d1.JOIN_ID, d2.JOIN_ID, d3.JOIN_ID)
適用された前の結合出力です。デカルト積の基数は、単純に481577 * 94025 = 45280277425です。
そのために、述部の選択性を決定して適用する必要があります。不透明な拡張COALESCE
ツリー(UNION
およびIIF
、覚えている)の組み合わせとキー情報への影響、派生したヒストグラム、および以前の「異常な」ほとんど冗長な多対多外部結合の頻度は、CEができないことを意味します。通常の方法で受け入れ可能な推定値を導き出します。
その結果、推測ロジックに入ります。推測ロジックは適度に複雑で、「教育された」推測と「それほど教育されていない」推測アルゴリズムのレイヤーが試行されました。推測のより良い根拠が見つからない場合、モデルは最後の手段の推測を使用します。これは、等価比較の場合:sqllang!x_Selectivity_Equal
=固定0.1選択性(10%推測):
-- the moment of doom
movsd xmm0,mmword ptr [sqllang!x_Selectivity_Equal
結果は、前述のように、デカルト積で0.1の選択性:481577 * 94025 * 0.1 = 4528027742.5(〜4.52803e + 009)です。
書き直し
問題のある結合がコメント化されると、固定選択性の「最後の手段の推測」が回避されるため、より適切な推定値が生成されます(キー情報は1-M結合によって保持されます)。COALESCE
結合述部はCEフレンドリではないため、推定の品質は依然として低い信頼性です。修正された推定値は、少なくとも人間にとってより合理的に見えると思います。
X_DETAIL_LINK
最後に配置された外部結合を使用してクエリが記述されている場合、ヒューリスティックな並べ替えは、それを最終的な内部結合と交換できX_LAST_TABLE
ます。インナーは右隣に参加置く問題の大部分は冗長「珍しい」多対多外の効果が来た結合するので、外部結合することは、早期の再注文の限られた能力に、最終的な推定値を改善する機会を与えた後、トリッキーな選択評価のためにCOALESCE
。繰り返しますが、推定値は固定された推測よりも少し良く、おそらく法廷での断固たる反対尋問に耐えられないでしょう。
内部結合と外部結合の混合を並べ替えるのは難しく、時間がかかります(ステージ2の完全な最適化でさえ、理論的な動きの限られたサブセットのみを試みます)。
ISNULL
Max Vernonの答えで入れ子になった提案は、救済の固定推測を避けることができますが、最終的な推定はありえないゼロ行です(品位のために1行に引き上げられます)。これは、計算に含まれるすべての統計的基礎について、1行の固定推測である可能性もあります。
0〜481577行の間の結合カーディナリティの推定値が予想されます。
物理的に異なるが、論理的および意味的に同一のサブツリーで異なる時期にカーディナリティの推定が発生する可能性があることを受け入れたとしても、これは合理的な期待です-最終計画は、ベスト(メモグループごと)。計画全体の一貫性の保証がないからといって、個々の参加者が敬意を払うことができるということにはなりません。
一方、最後の手段であると推測された場合、希望はすでに失われているので、なぜ気にするのでしょう。私たちは知っていたすべてのトリックを試し、あきらめました。それ以外の場合、最終的な予想は、このクエリのコンパイルおよび最適化中にすべてがCE内でうまくいかなかったという素晴らしい警告サインです。
MCVEを試してみたところ、120 + CE ISNULL
は元のクエリに対してゼロ(= 1)行の最終的な見積もり(ネストされたのような)を生成しましたが、これは私の考え方では受け入れられません。
実際のソリューションには、おそらく、COALESCE
またはのない単純な等結合を可能にするための設計変更が必要ISNULL
です。理想的には、クエリのコンパイルに役立つ外部キーおよびその他の制約があります。
bigint
の代わりに、decimal(18, 0)
あなたは利益を得るでしょう:1)使用8は、すべての値、および2のための代わりの9バイト)意味を持つ可能性がある、代わりにパックされたデータ型のバイト比較可能なデータ型を使用します値を比較するときのCPU時間。