SQL Server 2014:一貫性のない自己結合カーディナリティの推定値についての説明はありますか?


27

SQL Server 2014の次のクエリプランを検討してください。

ここに画像の説明を入力してください

クエリプランでは、自己結合ar.fId = ar.fIdにより1行の推定値が得られます。しかし、これは論理的に矛盾する推定値は、次のとおりar有する20,608行とのちょうど1つの異なる値fId(正確に統計に反映します)。したがって、この結合は行(~424MM行)の完全な外積を生成し、クエリを数時間実行します。

SQL Serverが統計と矛盾していることが非常に簡単に証明できる見積もりを思い付く理由を理解するのに苦労しています。何か案は?

初期調査と追加の詳細

ここでのPaulの回答に基づくと、結合のカーディナリティを推定するためのSQL 2012とSQL 2014の両方のヒューリスティックは、2つの同一のヒストグラムを比較する必要がある状況を簡単に処理できるようです。

トレースフラグ2363からの出力から始めましたが、それを簡単に理解できませんでした。以下は、SQL Serverがためにヒストグラムを比較していることを意味スニペットないfIdbIdだけその用途に参加の選択を推定するためにfId?もしそうなら、それは明らかに正しくないでしょう。または、トレースフラグの出力を読み間違えていますか?

Plan for computation:
  CSelCalcExpressionComparedToExpression( QCOL: [ar].fId x_cmpEq QCOL: [ar].fId )
Loaded histogram for column QCOL: [ar].bId from stats with id 3
Loaded histogram for column QCOL: [ar].fId from stats with id 1
Selectivity: 0

完全な再現スクリプトに含まれ、このクエリをミリ秒にまで下げるいくつかの回避策を思いついたことに注意してください。この質問は、動作の理解、今後のクエリでの動作の回避方法、およびMicrosoftに提出する必要があるバグかどうかの判断に焦点を当てています。

ここで完全なREPROスクリプトは、ここにあるトレースフラグ2363年からフル出力は、ここであなたがすぐにいっぱいスクリプトを開くことなく、それらを見たい場合はクエリとテーブル定義は次のとおりです。

WITH cte AS (
    SELECT ar.fId, 
        ar.bId,
        MIN(CONVERT(INT, ar.isT)) AS isT,
        MAX(CONVERT(INT, tcr.isS)) AS isS
    FROM  #SQL2014MinMaxAggregateCardinalityBug_ar ar 
    LEFT OUTER JOIN #SQL2014MinMaxAggregateCardinalityBug_tcr tcr
        ON tcr.rId = 508
        AND tcr.fId = ar.fId
        AND tcr.bId = ar.bId
    GROUP BY ar.fId, ar.bId
)
SELECT s.fId, s.bId, s.isS, t.isS
FROM cte s 
JOIN cte t 
    ON t.fId = s.fId 
    AND t.isT = 1

CREATE TABLE #SQL2014MinMaxAggregateCardinalityBug_ar (
    fId INT NOT NULL,
    bId INT NOT NULL,
    isT BIT NOT NULL
    PRIMARY KEY (fId, bId)
)

CREATE TABLE #SQL2014MinMaxAggregateCardinalityBug_tcr (
    rId INT NOT NULL,
    fId INT NOT NULL,
    bId INT NOT NULL,
    isS BIT NOT NULL
    PRIMARY KEY (rId, fId, bId, isS)
)

回答:


23

SQL Serverが統計と矛盾していることが非常に簡単に証明できる見積もりを思い付く理由を理解するのに苦労しています。

一貫性

一貫性の一般的な保証はありません。推定は、異なる統計手法を使用して、異なる時間に異なる(ただし論理的には同等の)サブツリーで計算できます。

これら2つの同一のサブツリーを結合するとクロス積が生成されるはずであるという論理には何の問題もありませんが、推論の選択が他のどの製品よりも健全であると言うことも同様にありません。

初期推定

特定のケースでは、結合の初期カーディナリティ推定は、2つの同一のサブツリーで実行されません。そのときの木の形は次のとおりです。

  LogOp_Join
     LogOp_GbAgg
        LogOp_LeftOuterJoin
           LogOp_Get TBL:ar
           LogOp_Select
              LogOp_Get TBL:tcr
              ScaOp_Comp x_cmpEq
                 ScaOp_Identifier [tcr] .rId
                 ScaOp_Const値= 508
           ScaOp_Logical x_lopAnd
              ScaOp_Comp x_cmpEq
                 ScaOp_Identifier [ar] .fId
                 ScaOp_Identifier [tcr] .fId
              ScaOp_Comp x_cmpEq
                 ScaOp_Identifier [ar] .bId
                 ScaOp_Identifier [tcr] .bId
        AncOp_PrjList 
           AncOp_PrjEl Expr1003 
              ScaOp_AggFunc stopMax
                 ScaOp_Convert int
                    ScaOp_Identifier [tcr] .isS
     LogOp_Select
        LogOp_GbAgg
           LogOp_LeftOuterJoin
              LogOp_Get TBL:ar
              LogOp_Select
                 LogOp_Get TBL:tcr
                 ScaOp_Comp x_cmpEq
                    ScaOp_Identifier [tcr] .rId
                    ScaOp_Const値= 508
              ScaOp_Logical x_lopAnd
                 ScaOp_Comp x_cmpEq
                    ScaOp_Identifier [ar] .fId
                    ScaOp_Identifier [tcr] .fId
                 ScaOp_Comp x_cmpEq
                    ScaOp_Identifier [ar] .bId
                    ScaOp_Identifier [tcr] .bId
           AncOp_PrjList 
              AncOp_PrjEl Expr1006 
                 ScaOp_AggFunc stopMin
                    ScaOp_Convert int
                       ScaOp_Identifier [ar] .isT
              AncOp_PrjEl Expr1007 
                 ScaOp_AggFunc stopMax
                    ScaOp_Convert int
                       ScaOp_Identifier [tcr] .isS
        ScaOp_Comp x_cmpEq
           ScaOp_Identifier Expr1006 
           ScaOp_Const値= 1
     ScaOp_Comp x_cmpEq
        ScaOp_Identifier QCOL:[ar] .fId
        ScaOp_Identifier QCOL:[ar] .fId

最初の結合入力には、投影されていない集約が単純化されており、2番目の結合入力には、そのt.isT = 1下にプッシュされる述部がt.isTありMIN(CONVERT(INT, ar.isT))ます。それにもかかわらず、isT述部の選択性計算CSelCalcColumnInIntervalはヒストグラムで使用できます。

  CSelCalcColumnInInterval
      列:COL:Expr1006 

列QCOLのロードされたヒストグラム:[ar] .isT、ID 3の統計から

選択性:4.85248e-005

生成された統計コレクション: 
  CStCollFilter(ID = 11、CARD = 1)
      CStCollGroupBy(ID = 10、CARD = 20608)
          CStCollOuterJoin(ID = 9、CARD = 20608 x_jtLeftOuter)
              CStCollBaseTable(ID = 3、CARD = 20608 TBL:ar)
              CStCollFilter(ID = 8、CARD = 1)
                  CStCollBaseTable(ID = 4、CARD = 28 TBL:tcr)

(正しい)予想は、この述部によって20,608行が1行に削減されることです。

結合推定

問題は、他の結合入力の20,608行がこの1行とどのように一致するかです。

  LogOp_Join
      CStCollGroupBy(ID = 7、CARD = 20608)
          CStCollOuterJoin(ID = 6、CARD = 20608 x_jtLeftOuter)
              ...

      CStCollFilter(ID = 11、CARD = 1)
          CStCollGroupBy(ID = 10、CARD = 20608)
              ...

      ScaOp_Comp x_cmpEq
          ScaOp_Identifier QCOL:[ar] .fId
          ScaOp_Identifier QCOL:[ar] .fId

一般に結合を推定するには、いくつかの異なる方法があります。たとえば、次のことができます。

  • 各サブツリーの各プラン演算子で新しいヒストグラムを導出し、それらを結合で整列させ(必要に応じてステップ値を補間)、それらがどのように一致するかを確認します。または
  • ヒストグラムのより単純な「粗い」アライメントを実行します(ステップバイステップではなく、最小値と最大値を使用します)。または
  • 結合列のみの個別の選択性を計算し(ベーステーブルから、フィルタリングなし)、非結合述語の選択性効果を追加します。
  • ...

使用中のカーディナリティー推定量およびいくつかのヒューリスティックに応じて、それらのいずれか(またはバリエーション)を使用できます。詳細については、Microsoftホワイトペーパー「SQL Server 2014 Cardinality Estimatorによるクエリプランの最適化」を参照してください。

バグ?

さて、質問で述べたように、この場合、「単純な」単一列結合(on fId)はCSelCalcExpressionComparedToExpression電卓を使用します。

計算の計画:

  CSelCalcExpressionComparedToExpression [ar] .fId x_cmpEq [ar] .fId

列QCOLのヒストグラムをロードしました:ID 2の統計から[ar] .bId
列QCOLのロードされたヒストグラム:ID 1の統計からの[ar] .fId

選択性:0

この計算では、20,608行と1つのフィルター処理された行を結合すると選択性がゼロになると評価されます。一致する行はありません(最終計画で1行として報告されます)。これは間違っていますか?はい、おそらく新しいCEにバグがあります。1つの行がすべての行に一致するか、まったく一致しないと考えることができるので、結果は妥当かもしれませんが、そうでなければ信じる理由があります。

詳細は実際にはかなり複雑ですがfId、フィルタの選択性によって変更され、20608 * 20608 * 4.85248e-005 = 20608行を提供する、フィルタ処理されていないヒストグラムに基づく推定値の期待は非常に合理的です。

この計算に従うことは、のCSelCalcSimpleJoinWithDistinctCounts代わりに計算機を使用することを意味しCSelCalcExpressionComparedToExpressionます。これを行うための文書化された方法はありませんが、興味がある場合は、文書化されていないトレースフラグ9479を有効にできます。

9479プラン

最終的な結合では、2つの単一行入力から20,608行が生成されますが、これは驚くべきことではありません。これは、TF 9481の下で元のCEによって作成された同じ計画です。

詳細は扱いにくい(そして調査に時間がかかる)と述べましたが、私が知る限り、問題の根本的な原因は、rId = 508選択性が0の述語に関連しています。このゼロ推定値は、通常の方法で1行に引き上げられます。これは、入力ツリー内の下位述語を考慮に入れると、問題の結合でゼロ選択性推定値に寄与するようです(したがって、の統計をロードしますbId)。

外部結合が(1行に上げるのではなく)ゼロ行の内側の推定値を保持できるようにする(したがって、すべての外側の行が適格になる)ことで、どちらの計算機でも「バグのない」結合推定値が得られます。これを調べることに興味がある場合、文書化されていないトレースフラグは9473(単独)です。

9473計画

結合カーディナリティの推定の動作は、CSelCalcExpressionComparedToExpression別の文書化されていないバリエーションフラグ(9494)で「bId」を考慮しないように変更することもできます。あなたがそのようなことに興味を持っていることを知っているので、これらすべてに言及します。解決策を提供するからではありません。マイクロソフトに問題を報告し、マイクロソフトが問題を解決する(またはしない)まで、クエリを異なる方法で表現することがおそらく最善の方法です。行動が意図的であるかどうかに関係なく、彼らは退行について耳を傾ける必要があります。

最後に、再生スクリプトで言及されているもう1つのことを整理するために、質問計画でのフィルターの最終位置はGbAggAfterJoinSel、結合出力の上に集約とフィルターを移動するコストベースの探索の結果です。行の数。予想どおり、フィルターは最初は結合の下にありました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.