アプリケーション内から使用する大きなビューがあります。私はパフォーマンスの問題を絞り込んだと思いますが、それを修正する方法がわかりません。ビューの簡略版は次のようになります。
SELECT ISNULL(SEId + '-' + PEId, '0-0') AS Id,
*,
DATEADD(minute, Duration, EventTime) AS EventEndTime
FROM (
SELECT se.SEId, pe.PEId,
COALESCE(pe.StaffName, se.StaffName) AS StaffName, -- << Problem!
COALESCE(pe.EventTime, se.EventTime) AS EventTime,
COALESCE(pe.EventType, se.EventType) AS EventType,
COALESCE(pe.Duration, se.Duration) AS Duration,
COALESCE(pe.Data, se.Data) AS Data,
COALESCE(pe.Field, se.Field) AS Field,
pe.ThisThing, se.OtherThing
FROM PE pe FULL OUTER JOIN SE se
ON pe.StaffName = se.StaffName
AND pe.Duration = se.Duration
AND pe.EventTime = se.EventTime
WHERE NOT(pe.ThisThing = 1 AND se.OtherThing = 0)
) Z
これはおそらくクエリ構造の理由全体を正当化するものではありませんが、おそらくあなたにアイデアを与えてくれます-このビューは、私が制御できない非常に不十分に設計された2つのテーブルを結合し、そこからいくつかの情報を合成しようとします。
したがって、これはアプリケーションから使用されるビューなので、最適化を試みている間、次のように別のSELECTでラップします。
SELECT * FROM (
-- … above code …
) Q
WHERE StaffName = 'SMITH, JOHN Q'
アプリケーションが結果で特定のスタッフメンバーを検索しているためです。
問題はCOALESCE(pe.StaffName, se.StaffName) AS StaffName
セクションのようで、私はのビューから選択していますStaffName
。それをpe.StaffName AS StaffName
またはに変更するとse.StaffName AS StaffName
、パフォーマンスの問題はなくなります(ただし、下記の更新された2を参照してください)。しかし、どちらか一方FULL OUTER JOIN
が欠落している可能性があるため、これはうまくいきません。一方または他方のフィールドがNULLになる可能性があります。
これをリファクタリングCOALESCE(…)
して別のものに置き換えることはできますか?サブクエリに書き直されますか?
その他の注意事項:
- クエリの残りの部分でのパフォーマンスの問題を修正するために、いくつかのインデックスをすでに追加しています
COALESCE
。 - 驚いたことに、ラッピングサブクエリと
WHERE
ステートメントが含まれている場合でも、実行プランを確認してもフラグは発生しません。アナライザーでのサブクエリの合計コストは0.0065736
です。ふん。実行には4秒かかります。 - アプリケーションを変更して別のクエリを実行する
(たとえば、を返すは機能するかもしれませんが、最後の手段として、アプリケーションに触れずにビューを最適化できることを本当に望んでいます。pe.StaffName AS PEStaffName, se.StaffName AS SEStaffName
と実行するWHERE PEStaffName = 'X' OR SEStaffName = 'X'
)こと - ストアドプロシージャはおそらくこれにとってより理にかなっていますが、アプリケーションはEntity Frameworkで構築されており、テーブル型(別のトピック全体)を返すSPでうまく機能させる方法を理解できませんでした。
インデックス
これまでに追加したインデックスは、次のようになります。
CREATE NONCLUSTERED INDEX [IX_PE_EventTime]
ON [dbo].[PE] ([EventTime])
INCLUDE ([StaffName],[Duration],[EventType],[Data],[Field],[ThisThing])
CREATE NONCLUSTERED INDEX [IX_SE_EventTime]
ON [dbo].[SE] ([EventTime])
INCLUDE ([StaffName],[Duration],[EventType],[Data],[Field],[OtherThing])
更新
うーん...私は上記の被災した変化をシミュレートしようとしましたが、それは助けにはなりませんでした。つまり、) Z
上記の前にを追加しましたAND (pe.StaffName = 'SMITH, JOHN Q' OR se.StaffName = 'SMITH, JOHN Q')
が、パフォーマンスは同じです。今、どこから始めればいいのか本当にわからない。
アップデート2
完全な結合が必要であるという@ypercubeのコメントから、合成クエリがおそらく重要なコンポーネントを省略していることがわかりました。はい、私は完全な結合が必要ですが、上記で私がドロップしCOALESCE
て結合の片側だけをテストしてnull以外の値をテストすると、完全な結合の反対側は無関係になり、オプティマイザはおそらくこれを使用していましたクエリを高速化するための事実。また、例を更新して、それStaffName
が実際に結合キーの1つであることを示しました-これはおそらく問題に大きく関係しています。私は現在、これを完全結合ではなく3方向の結合に分割することが答えになる可能性があり、COALESCE
とにかく行うsの数を単純化するという彼の提案にも寄りかかっています。今それを試してみてください。
KeyField
、両方の索引フィールドおよび他のいくつかのフィールドを。質問のインデックス定義を投稿できます。テストサーバーでこれに取り組んでいるので、試してみると役立つと思われるインデックスを追加できます。INCLUDE
StaffName
WHERE pe.ThisThing = 1 AND se.OtherThing = 0
キャンセル条件FULL OUTER
参加し、内部へのクエリと同等の参加になります。FULL参加が必要ですか?
INNER JOIN
、LEFT JOIN
とWHERE IS NULL
、チェックRIGHT IS NULLで登録しよう)し、その後、UNION ALL
三つの部分。これにより、使用する必要がなくなりCOALESCE()
、オプティマイザが書き換えを理解するのに役立つ場合があります(そうなる場合もあります)。