环境:Microsoft SQL Server 2016 (SP2-CU3)企业版
问题SQL:
select RowNumber = ROW_NUMBER() OVER ( -- This ordering is from the various Fulfillment Map sort orders to match the fulfillment app's row order. ORDER BY htly.LicenseYear, mht.Name, h.HuntFirstOpenDate, h.DisplayOrder, h.HuntCode, ci_orderby.LastName, ci_orderby.FirstName, fmu.FulfillmentMailingUnitID ), ShippingName = ISNULL(fism_aot.ShippingName, dbo.udf_GetCustomerName(c.CustomerID)), FulfillmentMailingUnitID = fmu.FulfillmentMailingUnitID, GoID = goid.IdentityValue, MailingZip = ISNULL(fism_zc.ZipCode, zc.ZipCode), TransactionID = fism_th.TransactionID, TransactionHeaderID = fism_th.TransactionHeaderID, HuntDate = h.HuntFirstOpenDate, HuntCode = h.HuntCode, -- Header info BatchNumber = fmulg.FulfillmentMailingUnitLockGroupID, PrintedByUserName = au.UserName, LockedDate = fmulg.LockedDate from dbo.FulfillmentMailingUnitLockGroup fmulg cross join dbo.Enum_IdentityType eit cross join dbo.Enum_LicenseActionType elat inner join dbo.FulfillmentMailingUnitLock fmul on fmulg.FulfillmentMailingUnitLockGroupID = fmul.FulfillmentMailingUnitLockGroupID inner join dbo.FulfillmentMailingUnit fmu on fmul.LockedFulfillmentMailingUnitID = fmu.FulfillmentMailingUnitID inner join dbo.ApplicationUser au on fmulg.LockedByApplicationUserID = au.ApplicationUserID -- Getting to the Transaction Header by FulfillmentInternetSalesMap OR FulfillmentDrawIssuanceMap left join dbo.FulfillmentInternetSalesMap fism on fmu.FulfillmentMailingUnitID = fism.FulfillmentMailingUnitID left join dbo.FulfillmentDrawIssuanceMap fdim on fmu.FulfillmentMailingUnitID = fdim.FulfillmentMailingUnitID left join dbo.TransactionHeader th on fism.TransactionHeaderID = th.TransactionHeaderID or fdim.TransactionHeaderID = th.TransactionHeaderID left join dbo.TransactionHeader fdim_th on fdim.TransactionHeaderID = fdim_th.TransactionHeaderID -- Getting to License from FulfillmentDrawNotificationMap left join dbo.FulfillmentDrawNotificationMap fdnm on fmu.FulfillmentMailingUnitID = fdnm.FulfillmentMailingUnitID left join dbo.DrawTicketLicense fdnm_dtl on fdnm.DrawTicketLicenseID = fdnm_dtl.DrawTicketLicenseID left join dbo.License fdnm_l on fdnm_dtl.LicenseID = fdnm_l.LicenseID left join dbo.DrawTicket fdnm_dt on fdnm_dtl.DrawTicketID = fdnm_dt.DrawTicketID left join dbo.DrawTicketHuntChoice fdnm_dthc on fdnm_dt.DrawTicketID = fdnm_dthc.DrawTicketID and ( -- If the draw ticket is a winner, link to the hunt choice that won. (fdnm_dt.WasDrawn = 1 and fdnm_dthc.WasDrawn = 1) -- Else if the draw ticket was not a winner, link to the first hunt choice since -- Losing and Alternate notifications are not valid for multi-choice hunts or (fdnm_dt.WasDrawn = 0 and fdnm_dthc.OrderIndex = 1) ) left join dbo.TransactionDetail fdim_td on fdim.TransactionHeaderID = fdim_td.TransactionHeaderID left join dbo.LicenseAction fdim_la on fdim_td.TransactionDetailID = fdim_la.TransactionDetailID -- This might be silly since it should only be Issued for issuance... (currently it's sold in the stored proc that issues tags) and (fdim_la.LicenseActionTypeID = elat.Sold or fdim_la.LicenseActionTypeID = elat.Issued or fdim_la.LicenseActionTypeID = elat.Duplicated) left join dbo.License fdim_l on fdim_la.LicenseID = fdim_l.LicenseID left join dbo.Hunt h on fdnm_dthc.HuntID = h.HuntID or fdim_l.HuntID = h.HuntID left join dbo.HuntTypeLicenseYear htly on h.HuntTypeLicenseYearID = htly.HuntTypeLicenseYearID left join dbo.MasterHuntType mht on htly.MasterHuntTypeID = mht.MasterHuntTypeID left join dbo.Customer c on fdnm_l.CustomerID = c.CustomerID or th.CustomerID = c.CustomerID left join dbo.CustomerIndividual ci on c.CustomerID = ci.CustomerID left join dbo.CustomerIdentity goid on c.CustomerID = goid.CustomerID and goid.IdentityTypeID = eit.GOID and goid.[Status] = 1 left join dbo.AddressDetail ad on c.MailingAddressID = ad.AddressID and ad.IsActive = 1 left join dbo.ZipCode zc on ad.ZipCodeID = zc.ZipCodeID left join dbo.CustomerIndividual ci_orderby on fdnm_l.CustomerID = ci_orderby.CustomerID or fdim_th.CustomerID = ci_orderby.CustomerID left join dbo.TransactionHeader fism_th on fism.TransactionHeaderID = fism_th.TransactionHeaderID left join dbo.ActiveOutdoorsTransaction fism_aot on fism_aot.TransactionID = fism_th.TransactionID left join dbo.AddressDetail fism_ad on fism_aot.ShippingAddressID = fism_ad.AddressID and fism_ad.IsActive = 1 left join dbo.ZipCode fism_zc on fism_ad.ZipCodeID = fism_zc.ZipCodeID where fmulg.FulfillmentMailingUnitLockGroupID = @FulfillmentMailingUnitLockGroupID
该SQL执行192s后出记录,分析一下sql的执行计划:
分析一:
最终的排序消耗了大量的cost:
分析二:
该SQL存在大量多表连接,MSSQL引擎由于统计信息的算法单一,在处理大量级联连接时,实际数据可能严重偏离统计信息
连接中存在Actual Rows和Estimated Rows严重不一致的情况,随着连接表数目增加,该不一致更加严重:
经过分析,优化的目标是减少多表连接的统计信息不一致导致的执行计划错误并且对最终的排序操作进行外推。
优化的手法主要是利用临时表固化统计信息,外推排序:
最终优化SQL:
select fmu.FulfillmentMailingUnitID ,elat.Sold ,elat.Issued ,elat.Duplicated ,fmulg.FulfillmentMailingUnitLockGroupID ,au.UserName ,fmulg.LockedDate ,eit.GOID into #temp from dbo.FulfillmentMailingUnitLockGroup fmulg cross join dbo.Enum_IdentityType eit cross join dbo.Enum_LicenseActionType elat inner join dbo.FulfillmentMailingUnitLock fmul on fmulg.FulfillmentMailingUnitLockGroupID = fmul.FulfillmentMailingUnitLockGroupID inner join dbo.FulfillmentMailingUnit fmu on fmul.LockedFulfillmentMailingUnitID = fmu.FulfillmentMailingUnitID inner join dbo.ApplicationUser au on fmulg.LockedByApplicationUserID = au.ApplicationUserID where fmulg.FulfillmentMailingUnitLockGroupID = @FulfillmentMailingUnitLockGroupID select fdnm_l.CustomerID fdnm_l_CustomerID, th.CustomerID th_CustomerID, fdim_th.CustomerID fdim_th_CustomerID, t.FulfillmentMailingUnitID, h.HuntFirstOpenDate, h.HuntCode, t.FulfillmentMailingUnitLockGroupID, t.UserName, LockedDate, t.GOID, htly.LicenseYear, mht.Name, h.DisplayOrder, --ci_orderby.LastName, --ci_orderby.FirstName, fism.TransactionHeaderID into #temp1 from #temp t -- Getting to the Transaction Header by FulfillmentInternetSalesMap OR FulfillmentDrawIssuanceMap left join dbo.FulfillmentInternetSalesMap fism on t.FulfillmentMailingUnitID = fism.FulfillmentMailingUnitID left join dbo.FulfillmentDrawIssuanceMap fdim on t.FulfillmentMailingUnitID = fdim.FulfillmentMailingUnitID left join dbo.TransactionHeader th on fism.TransactionHeaderID = th.TransactionHeaderID or fdim.TransactionHeaderID = th.TransactionHeaderID left join dbo.TransactionHeader fdim_th on fdim.TransactionHeaderID = fdim_th.TransactionHeaderID -- Getting to License from FulfillmentDrawNotificationMap left join dbo.FulfillmentDrawNotificationMap fdnm on t.FulfillmentMailingUnitID = fdnm.FulfillmentMailingUnitID left join dbo.DrawTicketLicense fdnm_dtl on fdnm.DrawTicketLicenseID = fdnm_dtl.DrawTicketLicenseID left join dbo.License fdnm_l on fdnm_dtl.LicenseID = fdnm_l.LicenseID left join dbo.DrawTicket fdnm_dt on fdnm_dtl.DrawTicketID = fdnm_dt.DrawTicketID left join dbo.DrawTicketHuntChoice fdnm_dthc on fdnm_dt.DrawTicketID = fdnm_dthc.DrawTicketID and ( -- If the draw ticket is a winner, link to the hunt choice that won. (fdnm_dt.WasDrawn = 1 and fdnm_dthc.WasDrawn = 1) -- Else if the draw ticket was not a winner, link to the first hunt choice since -- Losing and Alternate notifications are not valid for multi-choice hunts or (fdnm_dt.WasDrawn = 0 and fdnm_dthc.OrderIndex = 1) ) left join dbo.TransactionDetail fdim_td on fdim.TransactionHeaderID = fdim_td.TransactionHeaderID left join dbo.LicenseAction fdim_la on fdim_td.TransactionDetailID = fdim_la.TransactionDetailID -- This might be silly since it should only be Issued for issuance... (currently it's sold in the stored proc that issues tags) and (fdim_la.LicenseActionTypeID = t.Sold or fdim_la.LicenseActionTypeID = t.Issued or fdim_la.LicenseActionTypeID = t.Duplicated) left join dbo.License fdim_l on fdim_la.LicenseID = fdim_l.LicenseID left join dbo.Hunt h on fdnm_dthc.HuntID = h.HuntID or fdim_l.HuntID = h.HuntID left join dbo.HuntTypeLicenseYear htly on h.HuntTypeLicenseYearID = htly.HuntTypeLicenseYearID left join dbo.MasterHuntType mht on htly.MasterHuntTypeID = mht.MasterHuntTypeID --set statistics io on --set statistics time on select t1.LicenseYear, t1.Name, t1.DisplayOrder, c.CustomerID, t1.FulfillmentMailingUnitID, t1.GOID, zc.ZipCode, t1.HuntFirstOpenDate, t1.HuntCode, t1.FulfillmentMailingUnitLockGroupID, t1.UserName, t1.LockedDate, t1.fdnm_l_CustomerID, t1.fdim_th_CustomerID, t1.TransactionHeaderID into #temp2 from #temp1 t1 -- Getting to Cusotmer from the joined transaction header or the license from the DrawTicketLicense left join dbo.Customer c on t1.fdnm_l_CustomerID = c.CustomerID or t1.th_CustomerID = c.CustomerID left join dbo.CustomerIndividual ci on c.CustomerID = ci.CustomerID left join dbo.AddressDetail ad on c.MailingAddressID = ad.AddressID and ad.IsActive = 1 left join dbo.ZipCode zc on ad.ZipCodeID = zc.ZipCodeID select t2.LicenseYear, t2.Name, t2.DisplayOrder, ci_orderby.LastName, ci_orderby.FirstName, ShippingName = ISNULL(fism_aot.ShippingName, dbo.udf_GetCustomerName(t2.CustomerID)), FulfillmentMailingUnitID = t2.FulfillmentMailingUnitID, GoID = goid.IdentityValue, MailingZip = ISNULL(fism_zc.ZipCode, t2.ZipCode), TransactionID = fism_th.TransactionID, TransactionHeaderID = fism_th.TransactionHeaderID, HuntDate = t2.HuntFirstOpenDate, HuntCode = t2.HuntCode, -- Header info BatchNumber = t2.FulfillmentMailingUnitLockGroupID, PrintedByUserName = t2.UserName, LockedDate = t2.LockedDate into #temp3 from #temp2 t2 left join dbo.CustomerIdentity goid on t2.CustomerID = goid.CustomerID and goid.IdentityTypeID = t2.GOID and goid.[Status] = 1 left join dbo.CustomerIndividual ci_orderby on t2.fdnm_l_CustomerID = ci_orderby.CustomerID or t2.fdim_th_CustomerID = ci_orderby.CustomerID left join dbo.TransactionHeader fism_th on t2.TransactionHeaderID = fism_th.TransactionHeaderID left join dbo.ActiveOutdoorsTransaction fism_aot on fism_aot.TransactionID = fism_th.TransactionID left join dbo.AddressDetail fism_ad on fism_aot.ShippingAddressID = fism_ad.AddressID and fism_ad.IsActive = 1 left join dbo.ZipCode fism_zc on fism_ad.ZipCodeID = fism_zc.ZipCodeID select RowNumber = ROW_NUMBER() OVER ( -- This ordering is from the various Fulfillment Map sort orders to match the fulfillment app's row order. ORDER BY t3.LicenseYear, t3.Name, t3.HuntDate, t3.DisplayOrder, t3.HuntCode, t3.LastName, t3.FirstName, t3.FulfillmentMailingUnitID ), ShippingName, FulfillmentMailingUnitID, GoID, MailingZip, TransactionID, TransactionHeaderID, HuntDate, HuntCode, -- Header info BatchNumber, PrintedByUserName, LockedDate from #temp3 t3 drop table #temp drop table #temp1 drop table #temp2 drop table #temp3
经过测试,执行时间由192秒降低到2秒。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。