千家信息网

SQL调优的示例分析

发表于:2024-09-26 作者:千家信息网编辑
千家信息网最后更新 2024年09月26日,这篇文章主要为大家展示了"SQL调优的示例分析",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"SQL调优的示例分析"这篇文章吧。环境:Microsoft S
千家信息网最后更新 2024年09月26日SQL调优的示例分析

这篇文章主要为大家展示了"SQL调优的示例分析",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"SQL调优的示例分析"这篇文章吧。

环境: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.LockedDatefrom        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秒。

以上是"SQL调优的示例分析"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

0