0

我有一个查询,我从 5 个左外连接表中的每一个中选择几列。

我在 SQL Server 2008 中做了一个执行计划,基本上对所有连接的表都进行了表扫描,但它们的成本都是 0%——我假设是因为这些表中的记录不多。

然后在执行计划的最后两步(所有表的最终合并连接和实际的 SELECT 语句),它说 55% 的成本是合并连接,45% 的成本是选择。

这对我来说似乎很奇怪......为什么最后 2 个“将所有内容整合在一起”步骤的成本如此之高?我认为所有这些表扫描或排序步骤都会产生更大的成本。

我正在尝试从所有这些表中获取一个“汇总”记录......也许我在左加入所有内容时采取了错误的方法?

用 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),因此它应该更快。

我只做了其中两个,从这个例子中应该清楚第三个:

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

正如凯德所说,首先检查你的索引。

如果索引到位,请验证您的统计数据是最新的。

如果这两个问题都解决了,请考虑将您的子查询重构为一个或多个 CTE,然后按照相关标准加入它们。这不是灵丹妙药,但根据我的经验,CTE 通常比子查询执行得更好。

于 2009-11-25T20:05:00.227 回答
0

45% 的小东西仍然是 45%。没有看到更多细节很难说,但我发现在插入聚集(在非 IDENTITY 列上)索引表或具有大量索引的表时,最后阶段非常昂贵。

所有这些表扫描 - 没有索引吗?

于 2009-11-25T18:33:59.663 回答
0

如果您包含代码会很有帮助,但如果您有GROUP BYor 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 回答