您的位置:首页 > 数据库

SQL SERVER 2012 执行计划走嵌套循环导致性能问题的案例

2015-07-04 11:48 447 查看
开发人员遇到一个及其诡异的的SQL性能问题,这段完整SQL语句如下所示:

declare@UserIdINT
declare@PSANoVARCHAR(200)
declare@ShipModeVARCHAR(10)
declare@CY_FLAGVARCHAR(1)
declare@POVARCHAR(20)
declare@BuyerNameVARCHAR(100)
declare@DestinationVARCHAR(1)
declare@FinalDestinationVARCHAR(40)
declare@FactoryVARCHAR(10)
declare@NoticeDateStartDATETIME
declare@NoticeDateEndDATETIME
declare@EELForwarderVARCHAR(100)
declare@SortExpressionVARCHAR(100)
declare@RowIndexINT
declare@PageSizeINT
declare@ExistNoticeKeyvarchar(200)
DECLARE@NULLDATEDATETIME
SET@NULLDATE=GETDATE()
set@UserId=39
set@PSANo=''
set@ShipMode=''
set@CY_FLAG=''
set@PO=N''
set@BuyerName=N''
set@Destination=N''
set@FinalDestination=N''
set@Factory=''
set@EELForwarder=N''
set@SortExpression=''
set@RowIndex=0
set@PageSize=10
set@ExistNoticeKey=''
DECLARE@CountSqlNVARCHAR(max)
DECLARE@DataSqlNVARCHAR(max)
declare@nextint
declare@Where_PSANovarchar(400)
declare@Index_PSANovarchar(40)
declare@Where_ExcludeNotcekeyvarchar(400)
set@Where_PSANo=''
SETNOCOUNTON;
set@next=1
while@next<=dbo.Get_StrArrayLength(@PSANo,',')
begin
set@Index_PSANo=dbo.Get_StrArrayStrOfIndex(@PSANo,',',@next)
set@Where_PSANo=@Where_PSANo+'Ornotice.PSA_NOLIKE''%'+@Index_PSANo+'%'''
set@next=@next+1
end
set@Where_ExcludeNotcekey=''
if@ExistNoticeKey!=''
begin
set@Where_ExcludeNotcekey='ornotice.NOTICE_KEYnotin('+@ExistNoticeKey+')';
--select@Where_ExcludePSANo
--print'OK'
end
SELECTSUM(ISNULL(FactQty,0))ASFactQty,NOTICE_KEYINTO#TEMP
FROM
(
SELECTA.NOTICE_KEY,SUM(ISNULL(A.FactQty,0))FactQtyFROMIES.InvoiceFourLineAGROUPBYA.NOTICE_KEY
UNIONALL
SELECTA.NoticeKeyASNOTICE_KEY,SUM(ISNULL(A.FactQty,0))FactQtyFROMIES.InvoiceThreeByrFwdChargeLineAGROUPBYA.NoticeKey
)TGROUPBYNOTICE_KEY
SELECTCOUNT(*)
FROMIES.ExportNoticenotice--WITH(INDEX(PK_EXPORTNOTICE))
LEFTJOIN#TEMPtONnotice.NOTICE_KEY=T.NOTICE_KEY
WHERE
notice.FACTORY_CDIN(SELECTSiteIdFROMDCL.SecurityUserSiteMappingWHEREUserId=39)
AND(ISNULL(notice.FACT_EXPORT_QTY,0)-ISNULL(T.FactQty,0))>0
AND(ISNULL(@PSANo,'')=''Ornotice.PSA_NOLIKE'%%')
AND(ISNULL(@ExistNoticeKey,'')='')
AND(ISNULL(@ShipMode,'')=''ORnotice.SHIP_MODE_CD=@ShipMode)
AND(ISNULL(@CY_FLAG,'')=''ORnotice.CY_FLAG=@CY_FLAG)
AND(ISNULL(@PO,'')=''ORnotice.BUYER_PO_NOLIKE'%'+@PO+'%')
AND(ISNULL(@BuyerName,'')=''ORnotice.NAMELIKE'%'+@BuyerName+'%')
AND(ISNULL(@Destination,'')=''ORnotice.SZ=@Destination)
AND(ISNULL(@FinalDestination,'')=''ORnotice.FINAL_DESTINATIONLIKE'%'+@FinalDestination+'%')
AND(ISNULL(@Factory,'')=''ORnotice.FACTORY_CD=@Factory)
AND(ISNULL(@EELForwarder,'')=''ORnotice.EEL_FORWARDER=@EELForwarder)
AND(ISNULL(@NoticeDateStart,'2000-01-01')='2000-01-01')
---AND(ISNULL(@NoticeDateEnd,'1999-01-01')='1999-01-01')
DROPTABLE#TEMP

案例的环境为SQLSERVER2012StandardEdition(64-bit),具体版本号为11.0.5058.0,另外表IES.ExportNotice的数据记录为2万多。表IES.InvoiceThreeByrFwdChargeLine的记录数为1万多,表IES.InvoiceFourLine的记录只有区区几十条。临时表#TEMP的记录为1万多条。



执行上面SQL语句一般一秒以内完成。但是这段SQL如果将最后注释的条件加上(也就是最后注释的语句取消注释)

SELECTCOUNT(*)
FROMIES.ExportNoticenotice--WITH(INDEX(PK_EXPORTNOTICE))
LEFTJOIN#TEMPtONnotice.NOTICE_KEY=T.NOTICE_KEY
WHERE
notice.FACTORY_CDIN(SELECTSiteIdFROMDCL.SecurityUserSiteMappingWHEREUserId=39)
AND(ISNULL(notice.FACT_EXPORT_QTY,0)-ISNULL(T.FactQty,0))>0
AND(ISNULL(@PSANo,'')=''Ornotice.PSA_NOLIKE'%%')
AND(ISNULL(@ExistNoticeKey,'')='')
AND(ISNULL(@ShipMode,'')=''ORnotice.SHIP_MODE_CD=@ShipMode)
AND(ISNULL(@CY_FLAG,'')=''ORnotice.CY_FLAG=@CY_FLAG)
AND(ISNULL(@PO,'')=''ORnotice.BUYER_PO_NOLIKE'%'+@PO+'%')
AND(ISNULL(@BuyerName,'')=''ORnotice.NAMELIKE'%'+@BuyerName+'%')
AND(ISNULL(@Destination,'')=''ORnotice.SZ=@Destination)
AND(ISNULL(@FinalDestination,'')=''ORnotice.FINAL_DESTINATIONLIKE'%'+@FinalDestination+'%')
AND(ISNULL(@Factory,'')=''ORnotice.FACTORY_CD=@Factory)
AND(ISNULL(@EELForwarder,'')=''ORnotice.EEL_FORWARDER=@EELForwarder)
AND(ISNULL(@NoticeDateStart,'2000-01-01')='2000-01-01')
AND(ISNULL(@NoticeDateEnd,'1999-01-01')='1999-01-01')

然后执行时发现SQL慢得令人发指,非常的不可以思议。如果按照我们理解,这个条件(ISNULL(@NoticeDateEnd,'1999-01-01')='1999-01-01')仅仅相当于一个1=1或1=0的条件,怎么会有如此大的性能差距呢?查看执行计划后,发现加上这样一个条件后,执行计划完全不同了。



我姑且将执行性能较好的SQL的执行计划叫做PlanA,执行性能很差的SQL的执行计划叫做PlanB
PlanA



PlanB



如上所示,PlanB看似开销都耗费在键查找那一块,但是如果查看具体信息(如下所示),并无特别地方。



于是我使用HINT,强制在表IES.ExportNotice上走索引PK_EXPORTNOTICE,结果发现执行时,执行速度依然慢的令人发指。我觉得执行计划有些问题,Cost可能并不正确。

SELECTCOUNT(*)
FROMIES.ExportNoticenoticeWITH(INDEX(PK_EXPORTNOTICE))
LEFTJOIN#TEMPtONnotice.NOTICE_KEY=T.NOTICE_KEY
WHERE
notice.FACTORY_CDIN(SELECTSiteIdFROMDCL.SecurityUserSiteMappingWHEREUserId=39)
AND(ISNULL(notice.FACT_EXPORT_QTY,0)-ISNULL(T.FactQty,0))>0
AND(ISNULL(@PSANo,'')=''Ornotice.PSA_NOLIKE'%%')
AND(ISNULL(@ExistNoticeKey,'')='')
AND(ISNULL(@ShipMode,'')=''ORnotice.SHIP_MODE_CD=@ShipMode)
AND(ISNULL(@CY_FLAG,'')=''ORnotice.CY_FLAG=@CY_FLAG)
AND(ISNULL(@PO,'')=''ORnotice.BUYER_PO_NOLIKE'%'+@PO+'%')
AND(ISNULL(@BuyerName,'')=''ORnotice.NAMELIKE'%'+@BuyerName+'%')
AND(ISNULL(@Destination,'')=''ORnotice.SZ=@Destination)
AND(ISNULL(@FinalDestination,'')=''ORnotice.FINAL_DESTINATIONLIKE'%'+@FinalDestination+'%')
AND(ISNULL(@Factory,'')=''ORnotice.FACTORY_CD=@Factory)
AND(ISNULL(@EELForwarder,'')=''ORnotice.EEL_FORWARDER=@EELForwarder)
AND(ISNULL(@NoticeDateStart,'2000-01-01')='2000-01-01')
AND(ISNULL(@NoticeDateEnd,'1999-01-01')='1999-01-01')




于是我将怀疑的地方转移到表连接方式,使用TableHINT,强制下面SQL语句走HASHJOIN,结果SQL一秒钟执行完成。

SELECTCOUNT(*)
FROMIES.ExportNoticenotice
LEFTHASHJOIN#TEMPtONnotice.NOTICE_KEY=T.NOTICE_KEY
WHERE
notice.FACTORY_CDIN(SELECTSiteIdFROMDCL.SecurityUserSiteMappingWHEREUserId=39)
AND(ISNULL(notice.FACT_EXPORT_QTY,0)-ISNULL(T.FactQty,0))>0
AND(ISNULL(@PSANo,'')=''Ornotice.PSA_NOLIKE'%%')
AND(ISNULL(@ExistNoticeKey,'')='')
AND(ISNULL(@ShipMode,'')=''ORnotice.SHIP_MODE_CD=@ShipMode)
AND(ISNULL(@CY_FLAG,'')=''ORnotice.CY_FLAG=@CY_FLAG)
AND(ISNULL(@PO,'')=''ORnotice.BUYER_PO_NOLIKE'%'+@PO+'%')
AND(ISNULL(@BuyerName,'')=''ORnotice.NAMELIKE'%'+@BuyerName+'%')
AND(ISNULL(@Destination,'')=''ORnotice.SZ=@Destination)
AND(ISNULL(@FinalDestination,'')=''ORnotice.FINAL_DESTINATIONLIKE'%'+@FinalDestination+'%')
AND(ISNULL(@Factory,'')=''ORnotice.FACTORY_CD=@Factory)
AND(ISNULL(@EELForwarder,'')=''ORnotice.EEL_FORWARDER=@EELForwarder)
AND(ISNULL(@NoticeDateStart,'2000-01-01')='2000-01-01')
AND(ISNULL(@NoticeDateEnd,'1999-01-01')='1999-01-01')

虽然解决了问题,但是我隐隐觉得这应该是SQLSERVER优化器的某些Bug才导致出现这种特殊的情况。而且执行计划的Cost也完全不准确。让人有点匪夷所思。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: