查询中的慢排序

Posted

技术标签:

【中文标题】查询中的慢排序【英文标题】:Slow sort in query 【发布时间】:2018-12-02 01:02:00 【问题描述】:

所以我有这个查询

SELECT
    dbo.TQMNCR.NCRID,
    dbo.TQMPlantTable.PlantName AS 'Division',
    RTRIM(LTRIM(dbo.INVENTTABLE.ITEMGROUPID)) AS 'Item Process/Group',
    ISNULL(dbo.TQMNCRDEFECTTYPECODES.QTY,0) AS 'Defective Qty',
    CASE CATYPE
        WHEN 0 THEN 
            (CASE WHEN dbo.SALESLINE.SALESID = ''
                THEN ISNULL((PRICE * (PERCENTEXT / 100))  / NULLIF(dbo.INVENTTABLEMODULE.PRICEUNIT, 0), 0) * ISNULL(dbo.TQMNCRDEFECTTYPECODES.QTY,0)
                ELSE ISNULL((SALESPRICE * (PERCENTEXT / 100))  / NULLIF(dbo.SALESLINE.PRICEUNIT, 0), 0) * ISNULL(dbo.TQMNCRDEFECTTYPECODES.QTY,0) END)
        WHEN 2 THEN 
            (CASE WHEN dbo.TQMNCR.SALESID = ''
                THEN ISNULL((PRICE * (PERCENTINT / 100))  / NULLIF(dbo.INVENTTABLEMODULE.PRICEUNIT, 0), 0) * ISNULL(dbo.TQMNCRDEFECTTYPECODES.QTY,0)
                ELSE ISNULL((SALESPRICE * (PERCENTINT / 100))  / NULLIF(dbo.SALESLINE.PRICEUNIT, 0), 0) * ISNULL(dbo.TQMNCRDEFECTTYPECODES.QTY,0) END)
        ELSE 0 END AS 'Total Defective $',
    dbo.PRODTABLE.PRODPOOLID
    ,SCHED.qty,
    SUM(salesDollars.sales$) as 'sales Dollars',
    fc.YearMonth,
    HG.HighLevelItemGroupingCodeName
FROM
    dbo.TQMNCR 
    LEFT OUTER JOIN dbo.TQMDISPOSITION ON dbo.TQMNCR.DISPOSITIONID = dbo.TQMDISPOSITION.DISPOSITIONID 
    LEFT OUTER JOIN dbo.TQMCA_TABLE ON dbo.TQMCA_TABLE.NCRID = dbo.TQMNCR.NCRID 
    LEFT OUTER JOIN dbo.TQMNCRDEFECTTYPECODES ON dbo.TQMNCR.NCRID = dbo.TQMNCRDEFECTTYPECODES.NCRID
    LEFT OUTER JOIN dbo.TQMPlantTable ON TQMPlantTable.PlantID  = dbo.TQMNCR.PlantID
    LEFT OUTER JOIN dbo.INVENTTABLE ON dbo.TQMNCR.ITEMID = dbo.INVENTTABLE.ITEMID 
    LEFT OUTER JOIN dbo.INVENTTABLEMODULE ON dbo.INVENTTABLE.ITEMID = dbo.INVENTTABLEMODULE.ITEMID AND MODULETYPE = 2 
    LEFT OUTER JOIN dbo.SALESLINE ON dbo.SALESLINE.SALESID = dbo.TQMNCR.SALESID AND dbo.SALESLINE.ITEMID = dbo.TQMNCR.ITEMID 
    LEFT OUTER JOIN dbo.PRODTABLE ON dbo.TQMNCR.PRODID = dbo.PRODTABLE.PRODID
    inner join sched on sched.itemGroup = INVENTTABLE.itemgroupid
    inner join salesQty on salesQty.itemGroup = INVENTTABLE.itemgroupid
    inner join salesDollars on  salesDollars.itemgroup = INVENTTABLE.itemgroupid
    LEFT JOIN [MiscReportTables].[dbo].[FiscalCalendar] fc on SCHEDDATE between fc.StartDate and fc.EndDate
    left JOIN INVENTITEMGROUP IG on dbo.INVENTTABLE.ITEMGROUPID = IG.ITEMGROUPID
    LEFT JOIN Pmf_HighLevelItemGrouping HG on IG.HIGHLEVELITEM = HG.HighLevelItemGroupingCode
WHERE
    SCHEDDATE between @start1 and @end1
    AND
    dbo.TQMNCR.PlantID IN (SELECT [PLANTID]
                            FROM [Dynamics].[dbo].[TQMPLANTTABLE])
Group By TQMNCR.NCRID,TQMPlantTable.PLANTNAME,INVENTTABLE.ITEMGROUPID,TQMNCRDEFECTTYPECODES.QTY,TQMNCR.CATYPE,SALESLINE.SALESID,INVENTTABLEMODULE.PRICE,
TQMDISPOSITION.PERCENTEXT,INVENTTABLEMODULE.PRICEUNIT,INVENTTABLEMODULE.PRICEUNIT,SALESLINE.SALESPRICE,SALESLINE.PRICEUNIT,TQMNCR.SALESID,TQMDISPOSITION.PERCENTINT,PRODTABLE.PRODPOOLID,
sched.qty,salesQty.salesQTY,fc.YearMonth,HG.HighLevelItemGroupingCodeName`

它运行缓慢,当我查看查询分析器时,我看到了这个

因此,排序(由于 SUM 和相应的 group by 需要)似乎导致此查询运行缓慢。无论如何我可以加快速度吗? group by 非常大,我需要在所有这些表上添加索引吗?

编辑:这是计划的链接:Explain Plan

【问题讨论】:

可以发一下执行计划吗?在这一点上,任何人都无法提供足够的信息来帮助您。 brentozar.com/pastetheplan 我在这里在黑暗中拍摄,但它就在这里。 [动态].[dbo].[TQMPLANTTABLE] 表中的 [PLANTID] 值是否唯一?如果是这样,请使用此表替换您在 dbo.TQMNCR 上执行的过滤,方法是将其从 WHERE 子句中删除并将其添加为连接。 @SeanLange 谢谢,不知道该网站存在。已添加链接 您最后一次更新统计信息是什么时候?您的估计和实际行数存在巨大差异。 我不能说它会解决问题,但差异很大。从估计的行到实际的数量级。这会对如何计算执行计划产生很大的影响。考虑到这些桌子的大小,在下班时间做这件事可能是一个不错的举措。 【参考方案1】:

您可以尝试通过子查询(或 CTE)在数十亿行上避免 HUGE GROUP BY

试试这样的:

SELECT
    dbo.TQMNCR.NCRID,
    dbo.TQMPlantTable.PlantName AS 'Division',
    RTRIM(LTRIM(dbo.INVENTTABLE.ITEMGROUPID)) AS 'Item Process/Group',
    ISNULL(dbo.TQMNCRDEFECTTYPECODES.QTY,0) AS 'Defective Qty',
    CASE CATYPE
        WHEN 0 THEN 
            (CASE WHEN dbo.SALESLINE.SALESID = ''
                THEN ISNULL((PRICE * (PERCENTEXT / 100))  / NULLIF(dbo.INVENTTABLEMODULE.PRICEUNIT, 0), 0) * ISNULL(dbo.TQMNCRDEFECTTYPECODES.QTY,0)
                ELSE ISNULL((SALESPRICE * (PERCENTEXT / 100))  / NULLIF(dbo.SALESLINE.PRICEUNIT, 0), 0) * ISNULL(dbo.TQMNCRDEFECTTYPECODES.QTY,0) END)
        WHEN 2 THEN 
            (CASE WHEN dbo.TQMNCR.SALESID = ''
                THEN ISNULL((PRICE * (PERCENTINT / 100))  / NULLIF(dbo.INVENTTABLEMODULE.PRICEUNIT, 0), 0) * ISNULL(dbo.TQMNCRDEFECTTYPECODES.QTY,0)
                ELSE ISNULL((SALESPRICE * (PERCENTINT / 100))  / NULLIF(dbo.SALESLINE.PRICEUNIT, 0), 0) * ISNULL(dbo.TQMNCRDEFECTTYPECODES.QTY,0) END)
        ELSE 0 END AS 'Total Defective $',
    dbo.PRODTABLE.PRODPOOLID,
    SCHED.qty,
    salesDollars.[sales Dollars],
    fc.YearMonth,
    HG.HighLevelItemGroupingCodeName
FROM
    dbo.TQMNCR 
    LEFT OUTER JOIN dbo.TQMDISPOSITION ON dbo.TQMNCR.DISPOSITIONID = dbo.TQMDISPOSITION.DISPOSITIONID 
    LEFT OUTER JOIN dbo.TQMCA_TABLE ON dbo.TQMCA_TABLE.NCRID = dbo.TQMNCR.NCRID 
    LEFT OUTER JOIN dbo.TQMNCRDEFECTTYPECODES ON dbo.TQMNCR.NCRID = dbo.TQMNCRDEFECTTYPECODES.NCRID
    LEFT OUTER JOIN dbo.TQMPlantTable ON TQMPlantTable.PlantID  = dbo.TQMNCR.PlantID
    LEFT OUTER JOIN dbo.INVENTTABLE ON dbo.TQMNCR.ITEMID = dbo.INVENTTABLE.ITEMID 
    LEFT OUTER JOIN dbo.INVENTTABLEMODULE ON dbo.INVENTTABLE.ITEMID = dbo.INVENTTABLEMODULE.ITEMID AND MODULETYPE = 2 
    LEFT OUTER JOIN dbo.SALESLINE ON dbo.SALESLINE.SALESID = dbo.TQMNCR.SALESID AND dbo.SALESLINE.ITEMID = dbo.TQMNCR.ITEMID 
    LEFT OUTER JOIN dbo.PRODTABLE ON dbo.TQMNCR.PRODID = dbo.PRODTABLE.PRODID
    inner join sched on sched.itemGroup = INVENTTABLE.itemgroupid
    inner join salesQty on salesQty.itemGroup = INVENTTABLE.itemgroupid
    inner join (
        select itemgroup, SUM(sales$) as 'sales Dollars'
        from salesDollars
    ) salesDollars on  salesDollars.itemgroup = INVENTTABLE.itemgroupid
    LEFT JOIN [MiscReportTables].[dbo].[FiscalCalendar] fc on SCHEDDATE between fc.StartDate and fc.EndDate
    left JOIN INVENTITEMGROUP IG on dbo.INVENTTABLE.ITEMGROUPID = IG.ITEMGROUPID
    LEFT JOIN Pmf_HighLevelItemGrouping HG on IG.HIGHLEVELITEM = HG.HighLevelItemGroupingCode
WHERE
    SCHEDDATE between @start1 and @end1
    AND dbo.TQMNCR.PlantID IN (
        SELECT [PLANTID]
        FROM [Dynamics].[dbo].[TQMPLANTTABLE]
    )

【讨论】:

以上是关于查询中的慢排序的主要内容,如果未能解决你的问题,请参考以下文章

存储优化-排序引起的慢查询优化

laravel 中的慢查询

Django 慢查询:将 django 过滤语句连接到数据库日志中的慢查询

Postgres 中的慢查询优化

大数据库中的慢查询

优化 WordPress 插件“更好的 WordPress 最近评论”中的慢查询