2017-04-21 33 views
0

次のSQLクエリがあり、実行計画を使用して最適化しようとしています。実行計画では、推定サブツリーコストは36.89です。いくつかのテーブルスプール(Eager Spool)があります。誰も私がこのクエリを最適化するのを助けることができます。前もって感謝します。SQL Serverでテーブルスプールを最適化する実行計画

SELECT 
    COUNT(DISTINCT bp.P_ID) AS total, 
    COUNT(DISTINCT CASE WHEN bc.Description != 'S' THEN bp.P_ID END) AS m_count, 
    COUNT(DISTINCT CASE WHEN bc.Description = 'S' THEN bp.P_ID END) AS s_count, 
    COUNT(DISTINCT CASE WHEN bc.Description IS NULL THEN bp.P_ID END) AS n_count 
FROM 
    progress_tbl AS progress 
INNER JOIN Person_tbl AS bp ON bp.P_ID = progress.person_id 
LEFT OUTER JOIN Status_tbl AS bm ON bm.MS_ID = bp.MembershipStatusID   
LEFT OUTER JOIN Membership_tbl AS m ON m.M_ID = bp.CurrentMembershipID 
LEFT OUTER JOIN Category_tbl AS bc ON bc.MC_ID = m.MembershipCategoryID 
WHERE 
    logged_when BETWEEN '2017-01-01' AND '2017-01-31' 

enter image description here

+2

「b」エイリアスが表示されません。私は 'b_p'エイリアスがSELECT部分​​で使われているのを見ていません。投稿されたクエリで何かが間違っています – StanislavL

+0

投稿する前にクエリを編集しました。私はそれに応じてそれを変更します –

+0

作業クエリを提供したので、私の編集 –

答えて

0

すでに指摘したように、あなたのクエリといくつかのタイプミス/コピーペーストの問題があるように思われます。これにより、何が起こっているのか把握することがむずかしくなります。

テーブルスプールは、おそらくCASE WHEN b.description etc...構成で行われていることです。 MSSQLは最初にすべての結果の値を含む(メモリ)テーブルを作成し、次にそれはソートされ、COUNT(DISTINCT ...)演算子を介してストリーミングされます。私は仕事がどこかで行われる必要があるので、それについてあなたができることはたくさんあるとは思わない。

とにかく、いくつかの発言や野生の推測:

  • 私はlogged_whenprogress_tblテーブルであることを推測していますか?
  • もしそうなら、他のすべてのテーブルをLEFT OUTER JOINする必要がありますか?私は彼らが使用されていないと言うことができますから?
  • 基準に一致するP_IDの数を数えようとしていて、b.Descriptionのいずれか、それ以外のもの、またはNULLの間でその数値を分割したいとします。
    • この場合、合計はm_count、s_count、およびn_countの合計として計算できます。これは1 COUNT()の操作を節約しますが、大きな画像では多くの助けになりますが、すべてのビットが私の推測に役立ちます。

このような何か:

;WITH counts AS (

        SELECT 
         COUNT(DISTINCT CASE WHEN b.Description != 'S' THEN b_p.P_ID END) AS m_count, 
         COUNT(DISTINCT CASE WHEN b.Description = 'S' THEN b_p.P_ID END) AS s_count, 
         COUNT(DISTINCT CASE WHEN b.Description IS NULL THEN b_p.P_ID END) AS n_count 
        FROM 
         progress_tbl AS progress 
        INNER JOIN Person_tbl AS bp ON bp.P_ID = progress.person_id 
        LEFT OUTER JOIN Status_tbl AS bm ON bm.MS_ID = bp.MembershipStatusID  -- really needed? 
        LEFT OUTER JOIN Membership_tbl AS m ON m.M_ID = bp.CurrentMembershipID -- really needed? 
        LEFT OUTER JOIN Category_tbl AS bc ON bc.MC_ID = m.MembershipCategoryID -- really needed? 
        WHERE 
         logged_when BETWEEN '2017-01-01' AND '2017-01-31' -- what table does logged_when column come from???? 

    ) 

SELECT total = m_count + s_count + n_count, 
     * 
    FROM counts 

UPDATE

用心:マーティン・スミスの答え/サンプルコードを使用して、私はtotalは必ずしも合計ではないことを実感するようになりました他のフィールドの与えられたP_IDが異なるdescriptionと表示され、異なるカテゴリに分類される可能性があります。あなたのデータによっては、私の答えは間違っているかもしれません。

+0

進捗テーブルにはカラムがあります。logged_when –

+0

bc.Descriptionがselectクエリで使用されているので、Category_tblを使用する必要があります。 Category_tblにアクセスするには、残りのテーブルが必要です。 –

+0

Im LEFT OUTER JOINを使用して、bc.Description == nullで一致しない行を取得して、非メンバを取得する –

1

ここでは、使用できる手法について説明します。

WITH T AS 
(
SELECT DISTINCT CASE 
        WHEN bc.Description != 'S' THEN 'M' 
        WHEN bc.Description = 'S' THEN 'S' 
        WHEN bc.Description IS NULL THEN 'N' 
       END AS type, 
       bp.P_ID 
FROM progress_tbl AS progress 
     INNER JOIN Person_tbl AS bp 
     ON bp.P_ID = progress.person_id 
     LEFT OUTER JOIN Status_tbl AS bm 
     ON bm.MS_ID = bp.MembershipStatusID 
     LEFT OUTER JOIN Membership_tbl AS m 
     ON m.M_ID = bp.CurrentMembershipID 
     LEFT OUTER JOIN Category_tbl AS bc 
     ON bc.MC_ID = m.MembershipCategoryID 
WHERE logged_when BETWEEN '2017-01-01' AND '2017-01-31' 
) 
SELECT COUNT(DISTINCT P_ID) AS total, 
     COUNT(CASE WHEN type= 'M' THEN P_ID END) AS m_count, 
     COUNT(CASE WHEN type= 'S' THEN P_ID END) AS s_count, 
     COUNT(CASE WHEN type= 'N' THEN P_ID END) AS n_count 
FROM T 

より簡単な例で説明します。

WITH T AS 
(
SELECT DISTINCT CASE 
        WHEN name != 'S' 
        THEN 'M' 
        WHEN name = 'S' 
        THEN 'S' 
        ELSE 'N' 
       END AS type, 
       number 
FROM master..spt_values 
) 
SELECT COUNT(DISTINCT number) AS total, 
     COUNT(CASE WHEN type= 'M' THEN number END) AS m_count, 
     COUNT(CASE WHEN type= 'S' THEN number END) AS s_count, 
     COUNT(CASE WHEN type= 'N' THEN number END) AS n_count 
FROM T 

がリライトに注意してください、次のようにあなたがそれを書き換えることができ、既存のクエリが

SELECT 
    COUNT(DISTINCT number) AS total, 
    COUNT(DISTINCT CASE WHEN name != 'S' THEN number END) AS m_count, 
    COUNT(DISTINCT CASE WHEN name = 'S' THEN number END) AS s_count, 
    COUNT(DISTINCT CASE WHEN name IS NULL THEN number END) AS n_count 
FROM master..spt_values; 

であると仮定してかなり安価負けましおよび計画がはるかに簡単です。

enter image description here

+0

ありがとうございました。あなたのものを試しました、それはサブツリーコスト36.98を26.87に減らします。これをさらに最適化できないと思っていますか?それとも、データのためにサブツリーコストをさらに削減できないのでしょうか? –

+0

@KapilaPerera - これはジョインの実行計画や 'WHERE'に依存するので、言い表せません。あなたが示した計画にはそれらが含まれていません。とにかくそれは別の質問になるでしょう。あなたがここで尋ねた質問は、スプールを取り除くことでした。 –

関連する問題