web-dev-qa-db-ja.com

なぜこの左結合は内部結合よりも速いのですか?

したがって、このクエリを調整しています。この例では、データに影響を与えずに内部結合を左結合に置き換えることができると確信しています。しかし、なぜこれが速いのかは完全にはわかりません。これがクエリです:

SELECT DISTINCT  cl.NAME                            AS company_name,
                     cl.id                              AS company_id,
                     ep.Plan__c                           AS plan_id,
                     ep.Employee__c,
                     ep.id,
                     do.Subcategory__c,
                     ep.Plan_Type__c,
                     pt.SubType,
                     Sum((pt.[shares] * fvh.[ValuePerShare])) AS TotalValue,
                     ppe.Deferral_Option__c,
                     dt.Defer_type_Code,
                     do.Short_Code__c,
                     pt.ContributionYear
  FROM   dbo.ParticipantTrades pt WITH (NOLOCK) 
        INNER JOIN dbo.PayoutPathElection ppe WITH (NOLOCK) 
                ON pt.payoutPathElectionID = ppe.Id 
        INNER JOIN dbo.DeferralOption do WITH (NOLOCK) 
                ON ppe.Deferral_Option__c = do.id 
        INNER JOIN dbo.EmployeePlan ep WITH (NOLOCK) 
                ON pt.employeePlan = ep.Id 
        LEFT JOIN dbo.DeferralType dt WITH (NOLOCK) 
                ON pt.deferralType = dt.defID
        INNER JOIN dbo.Fnc_lastfundvalue('2019-01-30') AS fvh 
                ON pt.fund = fvh.Fund
        INNER JOIN dbo.Clients cl with (NOLOCK)
                ON ep.Company__c = cl.Id
  WHERE ep.Company__c = '0017000001WL1HfAAL' AND ep.Plan_Type__c  LIKE '%' AND pt.tradeDate <= '2019-01-30'
  Group by   cl.NAME,
             cl.id,
             ep.Plan__c,
             ep.Employee__c,
             ep.id,
             do.Subcategory__c,
             ep.Plan_Type__c,
             pt.SubType,
             ppe.Deferral_Option__c,
             dt.Defer_type_Code,
             do.Short_Code__c,
             pt.ContributionYear

ボトルネックは、テーブル値関数結合(Fnc_lastfundvalue)です。左結合に変更する方が速い理由は、結合を並べ替えることができ、tempdbへの流出が少ないことです。以下は、INNER JOIN dbo.Fnc_lastfundvalue ..をLEFT JOIN dbo.Fnc_lastfundvalue ..に変更する前後のクエリプランです。

前(29秒): https://www.brentozar.com/pastetheplan/?id=Bk1Y-WqEE

後(3秒): https://www.brentozar.com/pastetheplan/?id=B1hoW-544

注意:上記の実行プランは開発ボックスで作成されます。運用サーバーはSQL Server 2008上にあります。

4
Rick

スロープランが最初に実行されたため、違いがあるように見えます。

遅い計画は、コールドキャッシュに対して明らかに動作しており、追加の物理読み取りが示され、高速の場合と比較して、15秒のPAGEIOLATCH_SH待機がさらに蓄積されました。

これは、TVF自体の実行(高速ケースでは5.5と比較して1.35秒かかりました)と、その結果を使用したより広い計画の両方に影響を与えたように見えます。

コメントで、2回目の実行で11秒かかったと言っています。これはまだ高速プラン(3.446秒)の3倍遅いため、すべてのパフォーマンスの違いを説明できません。

発生している主な問題は、マルチステートメントTVFのデフォルトの見積もりが不十分であることが原因です(互換性レベル2014/2016の100行と以前のバージョンの1の固定された推測)。実際には、TVFは1,715行を返します。

内部結合の場合、内部結合は結合性と可換性があるため、柔軟に並べ替えることができ、ツリーの最も深い部分に移動します。これは、1つの行が実際に返された場合に意味があり、計画内の他の結合の行数を早期に減らします。

実行計画は以下のとおりです。ピンク色の注釈は、XMLからの「実際の経過時間(ms)」です。

enter image description here

1行の見積もりの​​ため、dbo.ParticipantTradesに参加し、ネストされたループとルックアップを含むプランを選択することで、最初はひどく始まります。そのネストされたループの経過時間は13.976秒です(おそらくそのほとんどは、上流の流出ソートで、そこから行をリクエストするために待機するために費やされ、3.26秒はルックアップ自体に関連し、 IOは、そのオペレーターでの物理的な読み取りを待機します)。

923,646行が結合と推定1423.18から出力されます。この誤った推定は、計画内で3つのソートとハッシュ結合を通じて上向きに伝搬します(行が過小であるため)。

LEFT JOINを追加すると、自由に並べ替えることはできず、結合は計画のはるか上で発生します(問題のあるINNER JOINの場合、損傷が少なくなります)。とにかく、ここでは外部結合のセマンティクスが役立ちます。

TVFから出力される行数の推定値は1のままですが、これは直接関与している結合の推定値に悪影響を与えません(SQL Serverは、その結合から出力されるカーディナリティが結合する他のサブツリーと同じですが、これは実際に起こります。結合されていない行が通過するため、外部結合はこの数を減らすことができません-NULL列にfvhを使用するだけです).

外部結合プランにはまだカーディナリティ推定エラーがありますが、同じ大きさではなく、どこにもこぼれないように十分なメモリ許可を要求します。

このカーディナリティ推定の問題は、最新バージョンの インターリーブ実行 で解決されています。それまでの間(コメントで言うように、2008互換である必要があります)、TVFの結果を#tempテーブルに保存し、それに結合して中間結果(および列)のカウントを許可することで、手動でインターリーブできます。統計)考慮されます。

6
Martin Smith