0

5 つの左外部結合テーブルのそれぞれからいくつかの列を選択するクエリがあります。

私は SQL Server 2008 で実行計画を立てました。基本的に、結合されたすべてのテーブルでテーブル スキャンが行われますが、それらのコストはすべて 0% です。これらのテーブルには多くのレコードがないためだと思います。

次に、実行計画の最後の 2 ステップ (すべてのテーブルの最終マージ結合と実際の SELECT ステートメント) で、コストの 55% がマージ結合であり、コストの 45% が選択であると述べています。

これは私には奇妙に思えます...最後の2つの「すべてをまとめる」ステップのコストがなぜそれほど高いのですか? これらすべてのテーブル スキャンまたは並べ替えの手順には、より大きなコストがかかると思いました。

これらすべてのテーブルから「要約された」レコードを 1 つ取得しようとしています。

SQL で更新

SELECT
/* Names */
NM.EMPLID, NM.NAME_PREFIX, NM.LAST_NAME, NM.FIRST_NAME, NM.MIDDLE_NAME, NM.NAME_SUFFIX,
/* Directory Info */
DIR_PERSON.BIRTH_DT,
/* PERSDATA */
PERS.SEX, PERS.HIGHEST_EDUC_LVL,
/* DIVERS.ETHNIC */
ETHNIC.ETHNIC_GRP_CD,
/* TENURE */
TENURE.EMPLID, TENURE.TENURE_STATUS, TENURE.EG_GRANTED_DT, TENURE.EG_TENURE_HOME, 
TENURE.EG_TRACK_HIRE_DT, TENURE.EG_MAND_REVW_DT, TENURE.CODE,
/* VISA */
VISA.VISA_PERMIT_TYPE

FROM NAMES NM

/* ----- Table Joins ----- */
/* Directory Join */
LEFT OUTER JOIN DIR_PERSON ON DIR_PERSON.ID = NM.EMPLID

/* PERS_DATA Join */
LEFT OUTER JOIN PERS ON PERS.EMPLID = NM.EMPLID
AND PERS.EFFDT =(   SELECT MAX(PERS_CURRENT.EFFDT) FROM PERS_CURRENT 
                    WHERE PERS.EMPLID = PERS_CURRENT.EMPLID 
                    AND PERS_CURRENT.EFFDT <= GETDATE())
/* ETHNIC Join */                       
LEFT OUTER JOIN  ETHNIC ON ETHNIC.EMPLID = NM.EMPLID
AND ETHNIC.PRIMARY_INDICATOR = 'Y'

/* TENURE Join */
LEFT OUTER JOIN TENURE ON TENURE.EMPLID = NM.EMPLID

/* VISA Join */
LEFT OUTER JOIN VISA ON VISA.EMPLID = NM.EMPLID
AND VISA.EFFDT = (  SELECT MAX(VISA_CURRENT.EFFDT) FROM VISA_CURRENT
                    WHERE VISA.EMPLID = VISA_CURRENT.EMPLID 
                    AND VISA_CURRENT.EFFDT <= GETDATE())

/* ----- End Table Joins ----- */       

WHERE   NM.NAME_TYPE = 'PRI' 
    AND NM.EFFDT = (SELECT MAX(NM_CURRENT.EFFDT) FROM NM_CURRENT 
                    WHERE NM.EMPLID = NM_CURRENT.EMPLID 
                    AND NM.NAME_TYPE = NM_CURRENT.NAME_TYPE 
                    AND NM_CURRENT.EFFDT <= GETDATE());
4

5 に答える 5

2

アイデアをスピードアップ

サブクエリを取り除くために、クエリをリファクタリングしました (テストしていないため、タイプミスがある可能性があります)。ここでは、最初にすべての最大アイテム (1 x empl の数) を取得してから、メインの選択 (1 x empl の数) を実行します。これにより、クエリが O(N^3) から O(N) に変更されるため、より高速になるはずです。

私はそのうちの 2 つだけを実行しましたが、3 つ目はこの例から明らかです。

WITH mVisa AS
(
 SELECT MAX(VISA_CURRENT.EFFDT) as max, VISA_CURRENT.EMPID as EMPLID
 FROM VISA_CURRENT
 WHERE VISA_CURRENT.EFFDT <= GETDATE()
 GROUP BY VISA_CURRENT.EMPLID
), mPers AS
(
 SELECT MAX(PERS_CURRENT.EFFDT) as max, PERS_CURRENT.EMPLID
 FROM PERS_CURRENT
 AND PERS_CURRENT.EFFDT <= GETDATE())
 GROUP BY PERS_CURRENT.EMPLID
)
SELECT
/* Names */
NM.EMPLID, NM.NAME_PREFIX, NM.LAST_NAME, NM.FIRST_NAME, NM.MIDDLE_NAME, NM.NAME_SUFFIX,
/* Directory Info */
DIR_PERSON.BIRTH_DT,
/* PERSDATA */
PERS.SEX, PERS.HIGHEST_EDUC_LVL,
/* DIVERS.ETHNIC */
ETHNIC.ETHNIC_GRP_CD,
/* TENURE */
TENURE.EMPLID, TENURE.TENURE_STATUS, TENURE.EG_GRANTED_DT, TENURE.EG_TENURE_HOME, 
TENURE.EG_TRACK_HIRE_DT, TENURE.EG_MAND_REVW_DT, TENURE.CODE,
/* VISA */
VISA.VISA_PERMIT_TYPE

FROM NAMES NM

/* ----- Table Joins ----- */
/* Directory Join */
LEFT OUTER JOIN DIR_PERSON ON DIR_PERSON.ID = NM.EMPLID

/* PERS_DATA Join */
LEFT JOIN mPers ON NM.EMPLID = mPers.EMPLID 
LEFT OUTER JOIN PERS ON PERS.EMPLID = NM.EMPLID
AND PERS.EFFDT = mPers.max
/* ETHNIC Join */                                       
LEFT OUTER JOIN  ETHNIC ON ETHNIC.EMPLID = NM.EMPLID
AND ETHNIC.PRIMARY_INDICATOR = 'Y'

/* TENURE Join */
LEFT OUTER JOIN TENURE ON TENURE.EMPLID = NM.EMPLID

/* VISA Join */
LEFT JOIN mVisa ON NM.EMPLID = mVisa.EMPLID
LEFT OUTER JOIN VISA ON VISA.EMPLID = NM.EMPLID
AND VISA.EFFDT = mVisa.max

/* ----- End Table Joins ----- */       

WHERE   NM.NAME_TYPE = 'PRI' 
        AND NM.EFFDT = (SELECT MAX(NM_CURRENT.EFFDT) FROM NM_CURRENT 
                                        WHERE NM.EMPLID = NM_CURRENT.EMPLID 
                                        AND NM.NAME_TYPE = NM_CURRENT.NAME_TYPE 
                                        AND NM_CURRENT.EFFDT <= GETDATE());
于 2009-11-25T20:05:02.743 に答える
1

Cade が言ったように、まずインデックスを確認してください。

インデックスが配置されている場合は、統計が最新であることを確認してください。

これらの問題の両方が確認された場合は、サブクエリを 1 つ以上の CTE にリファクタリングし、関連する基準で結合することを検討してください。これは特効薬ではありませんが、私の経験では、CTE はサブクエリよりもパフォーマンスが優れていることがよくあります。

于 2009-11-25T20:05:00.227 に答える
0

小さなものの 45% は、まだ 45% です。詳細を確認しないとなんとも言えませんが、クラスター化された (IDENTITY 列以外の) インデックス テーブルまたは多数のインデックスを持つテーブルに挿入する場合、最終段階で非常にコストがかかることがわかりました。

これらすべてのテーブル スキャンで - インデックスはありませんか?

于 2009-11-25T18:33:59.663 に答える
0

コードを含めていただけると助かりますが、たとえばGROUP BYまたはがあると、クエリが大幅に追加されます。ORDER BY

最終選択が大きなテーブルで、他のテーブルが小さいだけでなく、メイン テーブルであまり使用されていない場合、単純な部分であっても、クエリの一部で 100% に到達する必要があります。

于 2009-11-25T18:37:17.423 に答える
0
WHERE   NM.NAME_TYPE = 'PRI' 
        AND NM.EFFDT = (SELECT MAX(NM_CURRENT.EFFDT) FROM NM_CURRENT 
                                        WHERE NM.EMPLID = NM_CURRENT.EMPLID 
                                        AND NM.NAME_TYPE = NM_CURRENT.NAME_TYPE 
                                        AND NM_CURRENT.EFFDT <= GETDATE());

あなたの 45% はここにあります。MN.NAME_TYPE と NM.EFFDT でインデックスを作成すると、45% 低下することがわかります。

メインの選択のコストにこのサブクエリが含まれている場合と含まれていない場合があります。そうである場合、それが実際の問題です。行ごとにそのクエリを再実行する必要があることを忘れないでください。

結合のリファクタリングについては、他のコメントを参照してください。

于 2009-11-25T19:45:27.243 に答える