したがって、このクエリを調整しています。この例では、データに影響を与えずに内部結合を左結合に置き換えることができると確信しています。しかし、なぜこれが速いのかは完全にはわかりません。これがクエリです:
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上にあります。
スロープランが最初に実行されたため、違いがあるように見えます。
遅い計画は、コールドキャッシュに対して明らかに動作しており、追加の物理読み取りが示され、高速の場合と比較して、15秒のPAGEIOLATCH_SH
待機がさらに蓄積されました。
これは、TVF自体の実行(高速ケースでは5.5
と比較して1.35
秒かかりました)と、その結果を使用したより広い計画の両方に影響を与えたように見えます。
コメントで、2回目の実行で11
秒かかったと言っています。これはまだ高速プラン(3.446
秒)の3倍遅いため、すべてのパフォーマンスの違いを説明できません。
発生している主な問題は、マルチステートメントTVFのデフォルトの見積もりが不十分であることが原因です(互換性レベル2014/2016の100
行と以前のバージョンの1
の固定された推測)。実際には、TVFは1,715
行を返します。
内部結合の場合、内部結合は結合性と可換性があるため、柔軟に並べ替えることができ、ツリーの最も深い部分に移動します。これは、1つの行が実際に返された場合に意味があり、計画内の他の結合の行数を早期に減らします。
実行計画は以下のとおりです。ピンク色の注釈は、XMLからの「実際の経過時間(ms)」です。
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
テーブルに保存し、それに結合して中間結果(および列)のカウントを許可することで、手動でインターリーブできます。統計)考慮されます。