为什么在SQLServer2008中SELECT语句占执行计划成本的45%?

为什么在SQLServer2008中SELECT语句占执行计划成本的45%?,sql,sql-server,sql-execution-plan,Sql,Sql Server,Sql Execution Plan,我有一个查询,从5个左侧外部联接表中的每一个表中选择几列 我在SQL Server 2008中做了一个执行计划,基本上所有连接的表都有表扫描,但是它们的成本都是0%——我假设是因为这些表中没有太多记录 然后在执行计划的最后两个步骤中,所有表的最终合并联接和实际的SELECT语句,它表示55%的成本是合并联接,45%的成本是SELECT 这对我来说似乎很奇怪…为什么最后两个步骤的成本如此之高?我认为所有这些表格扫描或排序步骤将有更大的成本 我试图从所有这些表中得到一个汇总记录…也许我在左键连接所有

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

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

然后在执行计划的最后两个步骤中,所有表的最终合并联接和实际的SELECT语句,它表示55%的成本是合并联接,45%的成本是SELECT

这对我来说似乎很奇怪…为什么最后两个步骤的成本如此之高?我认为所有这些表格扫描或排序步骤将有更大的成本

我试图从所有这些表中得到一个汇总记录…也许我在左键连接所有内容时采取了错误的方法

用SQL更新


小东西的45%仍然是45%。很难说没有看到更多细节,但我发现在插入非标识列聚集索引表或包含大量索引的表时,最后阶段的开销非常大


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

45%的小数据仍然是45%。很难说没有看到更多细节,但我发现在插入非标识列聚集索引表或包含大量索引的表时,最后阶段的开销非常大


对于所有这些表扫描,是否没有索引?

如果包含代码,这将很有帮助,但是如果您有一个GROUP BY或ORDER BY,例如,那么这将为查询添加大量内容


如果最终选择的是一个大表,而其他表不仅很小,而且在主表中使用得不多,那么您需要在查询的某些部分达到100%,即使它是一个简单的部分。

如果您包含代码,这将很有帮助,但是如果您有一个GROUP BY或ORDER BY,例如,那么这将给查询增加很多内容

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());
如果最终的select是一个大表,而其他的表不仅很小,而且在主表中使用得不多,那么您需要在查询的某些部分达到100%,即使它是一个简单的部分

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%的下降

它可能会也可能不会在主select的成本中包含此子查询-如果是,那么这才是真正的问题所在。请记住,它必须为每一行重新运行该查询

请参阅其他注释,了解对联接的重构

你的45%在这里。如果您在MN.NAME_TYPE和NM.EFFDT上创建索引,您将看到45%的下降

它可能会也可能不会在主select的成本中包含此子查询-如果是,那么这才是真正的问题所在。请记住,它必须为每一行重新运行该查询


请参阅重构到联接的其他注释。

正如Cade所说,首先检查索引

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


如果两个问题都检查出来,请考虑将子查询重构为一个或多个CTE,然后加入相关标准。这不是一个灵丹妙药,但根据我的经验,CTE的性能通常比子查询好。

正如Cade所说,首先检查索引

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

如果两个问题都检查出来,请考虑将子查询重构为一个或多个CTE,然后加入相关标准。这不是一个灵丹妙药,但根据我的经验,CTE的性能通常比子查询更好。

加快想法的速度

我已经重构了您的查询,但我没有测试它,所以可能会有输入错误来消除子查询。在这里,您首先获取所有最大项目1 x数量的emp,然后运行主select 1 x数量的emp。这会将您的查询从^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());
加速想法

我已经重构了您的查询,但我没有测试它,所以可能会有输入错误来消除子查询。在这里,您首先获取所有最大项目1 x数量的emp,然后运行主select 1 x数量的emp。这会将您的查询从^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());

这只是一个直接的选择,将这些表中的数据合并到一个键的汇总行中(在本例中为雇员)。我没有DB的管理员权限,创建DB时我也不在场,但当我通过SQL Server查看所有这些表时,我在这些表上都没有看到任何键或索引……但我不确定SQL Server是否向我展示了真相。我完全做到了这一点。您可能没有很好的统计数据,因此执行计划可能不准确-收集所有信息并与DBA联系

将这些表中的数据汇总到一个键的汇总行中(在本例中为员工)。我没有DB的管理员权限,创建DB时我也不在场,但当我通过SQL Server查看所有这些表时,我在这些表上都没有看到任何键或索引……但我不确定SQL Server是否向我展示了真相。我完全做到了这一点。您可能没有良好的统计数据,因此执行计划可能不准确-收集所有信息并与DBA联系。您可以发布.sqlplan,即为查询导出的执行计划吗?我所指的服务器上不支持SET-SHOWPLAN_XML,我想它是在2005年之前吗?,因此,我无法获得外观良好的导出计划。您能否发布.sqlplan,即查询的导出执行计划?我所指的服务器上不支持SET-SHOWPLAN_XML,我猜它是在2005年之前?因此,我无法获得外观良好的导出计划。我看得越多,就越想知道-子查询在做什么。。。你有三个-他们一直在划船。。。这一定是减慢查询速度的原因。联接或WHERE上的任何子查询都是因为我需要特定人员的表中的最新记录。我看得越多,就越想知道-子查询在做什么。。。你有三个-他们一直在划船。。。这一定是减慢查询速度的原因。连接或WHERE上的任何子查询都是因为我需要特定人员的表中的最新记录。没错!请看我的评论Brook-ON,而不是^2或更糟的+1,因为我不像我那么懒,并且实际重构了查询:完全正确!请参阅我的评论Brook-ON,而不是^2或更糟的+1,因为它不像我那么懒惰,并且实际上重构了查询:感谢Hogan-我指的服务器似乎不支持SHOWPLAN_XML,所以现在我担心它也不支持CTE。我将在周一尽快尝试…否则我将尝试与维护此数据库并希望优化某些内容的人员交谈…SQL 2008?然后它就可以工作了——showplan_xml需要dbo_owner或其他一些权限。CTE现在是语言的一部分,应该可以了。如果你是2005年之前的,那么你可以在tempname中插入,然后链接到那些表。啊,酷,那我就试试……看起来就像CTE!你能告诉我还有很多东西要学吗;谢谢霍根!服务器是SQLServer2000,但是临时表似乎工作得很好。SELECT仍然显示为成本的49%,但使用这种以表为先的方法获取最大值的方法,查询速度要快得多。谢谢你的帮助!感谢Hogan-我所指的服务器似乎不支持SHOWPLAN_XML,所以现在我担心它也不支持CTE。我将在周一尽快尝试…否则我将尝试与维护此数据库并希望优化某些内容的人员交谈…SQL 2008?然后它就可以工作了——showplan_xml需要dbo_owner或其他一些权限。CTE现在是语言的一部分,应该可以了。如果你是2005年之前的,那么你可以在tempname中插入,然后链接到那些表。啊,酷,那我就试试……看起来就像CTE!你能告诉我还有很多东西要学吗;谢谢霍根!服务器是SQLServer2000,但是临时表似乎工作得很好。SELECT仍然显示为成本的49%,但使用这种以表为先的方法获取最大值的方法,查询速度要快得多。谢谢你的帮助!