web-dev-qa-db-ja.com

フィルターされたインデックスは選択されず、ヒントで拒否されました

フィルター処理されたときに特定のクエリで非クラスター化インデックスを使用できない理由を理解しようとしています。私の(大きな)クエリの関連部分はこれです:

) results
JOIN BE_Insurance ins
ON results.PayorId = ins.Id

Selectでは、ins.nameだけを取得しています。最初に作成したインデックスは次のとおりです。

CREATE INDEX IX_BE_Insurance_PayorId_PayorName
ON      BE_Insurance (Id) INCLUDE (Name)
WHERE ParentId IS NULL

私のクエリでは、NULLのparentIdを持つpayorIdsのみが選択されるようになっていますが、オプティマイザがそれを選択する傾向がないことを理解しました。しかし、インデックスを強制しようとするヒントを追加すると、全体がエラーになります。

このクエリでヒントが定義されているため、クエリプロセッサはクエリプランを作成できませんでした。ヒントを指定せず、SET FORCEPLANを使用せずにクエリを再送信します。

私はそれが私のヒントに従うと思いました、そして私がいくつかの悪いデータで終わり、インデックスがいくつかの必要な値を欠いている場合、実行時にエラーになるかもしれません。インデックスからフィルターを削除すると、クエリで(ヒントなしでも)フィルターが正常に選択されました。

WHERE句のあるインデックスは、このようなクエリには不適格ですか?それらは、オプティマイザーが保証であり、フィルターが有効で欠落値を生成しない場合にのみ適格ですか?


要求どおり、ここにクエリ全体があります。これはまだ進行中の作業です。

SELECT results.*
FROM (
        SELECT auths.*,
               worked.WorkedHours,
               worked.WorkedUnits,
               worked.WorkedAmount,
               worked.ActiveClients
        FROM (
                SELECT PayorId,
                       SUM(AuthHours) AuthHours, 
                       SUM(AuthUnits) AuthUnits, 
                       SUM(AuthAmount) AuthAmount
                FROM (
                        --DYNAMIC TEMPLATE ----------------------------------------------------------------------------------
                        SELECT PayorId,
                               PayorName,
                               AuthHours AuthHours, 
                               AuthUnits AuthUnits, 
                               AuthAmount AuthAmount
                        FROM PayorAuthorizations_Level1Data_Authorizations auths WITH(NOEXPAND)
                        WHERE OrganizationId = @organizationId AND StartDate <= @endDate AND @startDate <= EndDate
                        --/DYNAMIC TEMPLATE----------------------------------------------------------------------------------

                        --INTERSECT / EXCEPT dynamically generated queries 
                ) q
                GROUP BY PayorId
        ) auths
        JOIN (
                SELECT PayorId,
                       SUM(ISNULL(TotalWorkedHours, 0)) WorkedHours,
                       SUM(ISNULL(TotalWorkedUnits, 0)) WorkedUnits,
                       SUM(ISNULL(TotalWorkedAmount, 0)) WorkedAmount,
                       COUNT(DISTINCT clientId) ActiveClients
                FROM (
                        --DYNAMIC TEMPLATE ----------------------------------------------------------------------------------
                        SELECT PayorId,                   
                               TotalWorkedHours,
                               TotalWorkedUnits,
                               TotalWorkedAmount,
                               clientId clientId
                        FROM PayorAuthorizations_Level1Data_CurrentlyWorked worked WITH(NOEXPAND)
                        WHERE OrganizationId = @organizationId AND StartDate <= @endDate AND @startDate <= EndDate
                        --/DYNAMIC TEMPLATE----------------------------------------------------------------------------------

                        --INTERSECT / EXCEPT other dynamically generated queries 
                ) q
                GROUP BY PayorId
        ) worked
        ON auths.payorId = worked.payorId
) results
JOIN BE_Insurance ins WITH (INDEX(IX_BE_Insurance_PayorId_PayorName))
ON results.PayorId = ins.Id
OPTION(FORCE ORDER, MERGE JOIN)
5
Adam Rackis

ヒントに従っていると想定しましたが、いくつかの不良データがあり、インデックスに必要な値がない場合、実行時にエラーになる可能性があります。

クエリオプティマイザーは、可能な限りすべての一致をインデックスから提供できることを(推論フレームワーク内で)guaranteeできる場合にのみ、クエリプランでフィルター選択されたインデックスを使用します。これは仕様によるものであり、説明するような実行時エラーを回避するためです。

おそらくクラスターIDを取得するために、クラスター化されていないインデックスからクラスター化されたインデックスのキー検索に対してNESTED LOOPS JOINが返されない。親IDを含めると、これが排除され、Nice非クラスター化インデックススキャンが残ります。

これは 既知の現在の制限 です。フィルターされた列をキーまたはインクルードリストに追加することが標準の回避策であり、あらゆる種類の準関連の理由に対する現在のベストプラクティスです。

FORCE ORDER、MERGE JOINは絶対に必要です。

完全に理解するすべての結果を除いて、このようなヒント(ディレクティブ)の使用には細心の注意を払ってください。 FORCE ORDERは特に、非常に強力で幅広いヒントであり、集計演算子の配置、サブクエリと共通テーブル式の評価順序など、多くの非自明な副作用があります。

ほとんどの場合、ヒントなしで正しい決定を行うために十分な品質の情報をクエリオプティマイザーに提供するクエリを作成するようにしてください。ヒント付きプランは今日「最適」である可能性がありますが、データ量や分布が時間の経過とともに変化するため、そうではない可能性があります。

8
Paul White 9

フィルター選択されたインデックスを使用するには、そこに述語、または非常によく一致する述語を表示する必要があります。したがって、明示的にAND ins.ParentID IS NULLが役立つでしょう。

ここで、QOの癖のため、通常はフィルタリングする列をインデックス自体に含める必要があります。クエリの述語がインデックスの述語と完全に一致し、他の場所で列を参照していない場合は、含まれている列が含まれないようにする必要があります。しかし、QOがその接続を完全に確立しない場合があり、念のためにインクルードされた列を投入するほうがよい場合があります。

3
Rob Farley