zoukankan      html  css  js  c++  java
  • 记一次SQL优化过程

    环境:Microsoft SQL Server 2016 (SP2-CU3)企业版

    问题SQL:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    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:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    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秒。

  • 相关阅读:
    【干货分享】嵌入式学习路线公开!(书籍推荐+视频推荐+练手项目)
    test
    pytest学习小结
    pytest运行报错 TypeError: attrib() got an unexpected keyword argument 'convert'
    pytest插件下载网址
    python修改文件内容的3种方法详解
    PermissionError: [Errno 13] Permission denied 如何解决
    Pycharm退出pytest模式(run pytest in模式)
    直流电机驱动电路设计-----学习笔记
    2019年全国大学生电子设计竞赛赛题分享与浅析
  • 原文地址:https://www.cnblogs.com/database/p/11649239.html
Copyright © 2011-2022 走看看