【问题标题】:SQL Server 2014 - Multiple SUM/COUNT with subqueriesSQL Server 2014 - 带有子查询的多个 SUM/COUNT
【发布时间】:2017-08-06 18:30:05
【问题描述】:

我有一个两部分的问题。

第一部分是关于我在下面做的事情的速度。我正在获取一个数据集并获取每个DISTINCT,然后加入一个按条件求和/计数的子查询,然后重复该操作五次。在第一个 LEFT JOIN 之后,我的运行时间为 02 秒,在第二个 LEFT JOIN 之后,我的运行时间为 04 秒,但在第三个 LEFT JOIN 之后,我的运行时间为 44 秒。如果将那个查询拉到一边单独运行它,它只是 01 秒。完成查询的总运行时间为 70 秒。

第一部分问题:

是什么导致此查询在第三次连接时显着变慢?

第二部分:我确信我以一种低效的方式完成了这项工作。我环顾了一会儿,无法找到一种方法来以简单的方式完成我正在做的事情,同时仍然能够为每个子查询设置不同的标准。

第二部分问题:

有没有更好的方法来完成这个查询?

在 user1413 发表评论后编辑

SELECT DISTINCT 一开始在 SQL Server 执行计划视图中显示 49% 的成本。我更改了 SELECT DISTINCT 从中选择的表,我的运行时间降至 01 秒以下。

SELECT DISTINCT 
    od.location, currentTires, currentAlignments, currentLubes, 
    currentBatteries,currentSiping,currentCarcount
FROM 
    comm.dbo.ordetail as od
LEFT JOIN 
    (SELECT 
         od2.location, SUM(od2.qty_shipped) as currentTires
     FROM 
         comm.dbo.ordetail as od2
     JOIN 
         comm.dbo.invmas as invmas2 ON invmas2.item_num = od2.item#
     JOIN 
         comm.dbo.ordhrd as oh2 ON oh2.location = od2.location
                                AND oh2.inv_date = od2.inv_date
                                AND oh2.invoice# = od2.invoice#
     WHERE 
         (oh2.type_of_rec = '01' OR oh2.type_of_rec = '02')
         AND od2.inv_date >= '2017-02-01 00:00:00.000'
         AND od2.inv_date <= '2017-02-28 00:00:00.000'
         AND ( invmas2.category = 'uhp' OR invmas2.category = 'tour' OR invmas2.category = 'mass' OR invmas2.category = 'suv' OR invmas2.category = 'ltat' OR invmas2.category = 'ltmt' OR invmas2.category = 'lthwy' OR invmas2.category = 'snow' OR invmas2.category = 'stdls' )
    GROUP BY 
        od2.location) as currentTires ON od.location = currentTires.location
LEFT JOIN 
    (SELECT 
         od3.location, SUM(od3.qty_shipped) as currentAlignments
     FROM 
         comm.dbo.ordetail as od3
     JOIN 
         comm.dbo.invmas as invmas3 ON invmas3.item_num = od3.item#
     JOIN 
         comm.dbo.ordhrd as oh3 ON oh3.location = od3.location
                                AND oh3.inv_date = od3.inv_date
                                AND oh3.invoice# = od3.invoice#
     WHERE 
         (oh3.type_of_rec = '01' OR oh3.type_of_rec = '02')
         AND od3.inv_date >= '2017-02-01 00:00:00.000'
         AND od3.inv_date <= '2017-02-28 00:00:00.000'
         AND (od3.item# = '8501' OR od3.item# = '8502')
     GROUP BY 
         od3.location) as currentAlignments ON od.location = currentAlignments.location
LEFT JOIN 
    (SELECT 
         od4.location, SUM(od4.qty_shipped) as currentLubes
     FROM 
         comm.dbo.ordetail as od4
     JOIN 
         comm.dbo.invmas as invmas4 ON invmas4.item_num = od4.item#
     JOIN 
         comm.dbo.ordhrd as oh4 ON oh4.location = od4.location
                                AND oh4.inv_date = od4.inv_date
                                AND oh4.invoice# = od4.invoice#
     WHERE 
         (oh4.type_of_rec = '01' OR oh4.type_of_rec = '02')
         AND od4.inv_date >= '2017-02-01 00:00:00.000'
         AND od4.inv_date <= '2017-02-28 00:00:00.000'
         AND (od4.item# = '200fs' OR od4.item# = '200c' OR od4.item# = '200m' OR od4.item# = '200s')
     GROUP BY 
         od4.location) as currentLubes ON od.location = currentLubes.location
    LEFT JOIN ( SELECT od5.location,SUM(od5.qty_shipped) as currentBatteries
        FROM comm.dbo.ordetail as od5
        JOIN comm.dbo.invmas as invmas5
            ON invmas5.item_num = od5.item#
        JOIN comm.dbo.ordhrd as oh5
            ON oh5.location = od5.location
            AND oh5.inv_date = od5.inv_date
            AND oh5.invoice# = od5.invoice#
        WHERE ( oh5.type_of_rec = '01' OR oh5.type_of_rec = '02' )
            AND od5.inv_date >= '2017-02-01 00:00:00.000'
            AND od5.inv_date <= '2017-02-28 00:00:00.000'
            AND invmas5.manufact = 'inter'
        GROUP BY od5.location) as currentBatteries
        ON od.location = currentBatteries.location
    LEFT JOIN ( SELECT od6.location,SUM(od6.qty_shipped) as currentSiping
        FROM comm.dbo.ordetail as od6
        JOIN comm.dbo.invmas as invmas6
            ON invmas6.item_num = od6.item#
        JOIN comm.dbo.ordhrd as oh6
            ON oh6.location = od6.location
            AND oh6.inv_date = od6.inv_date
            AND oh6.invoice# = od6.invoice#
        WHERE ( oh6.type_of_rec = '01' OR oh6.type_of_rec = '02' )
            AND od6.inv_date >= '2017-02-01 00:00:00.000'
            AND od6.inv_date <= '2017-02-28 00:00:00.000'
            AND invmas6.manufact = 'inter'
        GROUP BY od6.location) as currentSiping
        ON od.location = currentSiping.location
    LEFT JOIN ( SELECT od7.location,COUNT(DISTINCT oh7.invoice#) as currentCarcount
        FROM comm.dbo.ordetail as od7
        JOIN comm.dbo.ordhrd as oh7
            ON oh7.location = od7.location
            AND oh7.inv_date = od7.inv_date
            AND oh7.invoice# = od7.invoice#
        WHERE ( oh7.type_of_rec = '01' OR oh7.type_of_rec = '02' )
            AND od7.inv_date >= '2017-02-01 00:00:00.000'
            AND od7.inv_date <= '2017-02-28 00:00:00.000'
            AND oh7.veh_make != ''
            AND od7.item# != ''
        GROUP BY od7.location) as currentCarcount
        ON od.location = currentCarcount.location
    ORDER BY od.location

样本数据输出:

【问题讨论】:

  • 你检查执行计划了吗?
  • 我刚做了,我不熟悉我在看什么。分享它不是一个简单的方法。大量数据。 XML 输出为 5k 行。我是否应该识别高成本运营并找到减少成本的方法?
  • 我不是那个意思。 SSMS 中有一个图标。您甚至可以在 Query Menu --> Display Estimated Execution Plan 中找到它,这将根据显示的百分比帮助您在哪里花费更多时间。

标签: sql-server join count subquery


【解决方案1】:

这里有很多可以改进的地方。对于初学者,可以消除以下连接之一,因为 od5 和 od6 返回相同的项目。

LEFT JOIN ( SELECT od5.location,SUM(od5.qty_shipped) as currentBatteries
    FROM comm.dbo.ordetail as od5
    JOIN comm.dbo.invmas as invmas5
        ON invmas5.item_num = od5.item#
    JOIN comm.dbo.ordhrd as oh5
        ON oh5.location = od5.location
        AND oh5.inv_date = od5.inv_date
        AND oh5.invoice# = od5.invoice#
    WHERE ( oh5.type_of_rec = '01' OR oh5.type_of_rec = '02' )
        AND od5.inv_date >= '2017-02-01 00:00:00.000'
        AND od5.inv_date <= '2017-02-28 00:00:00.000'
        AND invmas5.manufact = 'inter'
    GROUP BY od5.location) as currentBatteries
    ON od.location = currentBatteries.location
LEFT JOIN ( SELECT od6.location,SUM(od6.qty_shipped) as currentSiping
    FROM comm.dbo.ordetail as od6
    JOIN comm.dbo.invmas as invmas6
        ON invmas6.item_num = od6.item#
    JOIN comm.dbo.ordhrd as oh6
        ON oh6.location = od6.location
        AND oh6.inv_date = od6.inv_date
        AND oh6.invoice# = od6.invoice#
    WHERE ( oh6.type_of_rec = '01' OR oh6.type_of_rec = '02' )
        AND od6.inv_date >= '2017-02-01 00:00:00.000'
        AND od6.inv_date <= '2017-02-28 00:00:00.000'
        AND invmas6.manufact = 'inter'
    GROUP BY od6.location) as currentSiping
    ON od.location = currentSiping.location

至于性能和可读性,请尝试以下内容。注意:我不可能对此进行测试,但是,它可能会让有更多时间来调整查询性能的人深入了解。

DECLARE @LowDate DATETIME = '2017-02-01 00:00:00.000'
DECLARE @HighDate DATETIME = '2017-02-28 00:00:00.000'


SELECT
    DetailList.location,
    currentTires = SUM(currentTires), 
    currentAlignments = SUM(currentAlignments),
    currentLubes = SUM(currentLubes),
    currentBatteries = SUM(currentBatteries),
    currentSiping = SUM(currentSiping),         
    carCount = SUM(hasCar)
FROM
(
    SELECT 
        od.location,
        currentLubes=CASE WHEN  ( od.item# = '200fs' OR od.item# = '200c' OR od.item# = '200m' OR od.item# = '200s' ) THEN od.qty_shipped ELSE NULL END,
        currentAlignments=CASE WHEN  ( od.item# = '8501' OR od.item# = '8502' ) THEN od.qty_shipped ELSE NULL END,
        currentSiping = CASE WHEN ( invmas.item_num = 'p15' OR invmas.item_num = 'u15' ) THEN od.qty_shipped ELSE NULL END,
        currentBatteries = CASE WHEN invmas.manufact = 'inter' THEN od.qty_shipped ELSE NULL END,
        currentTires=  CASE WHEN ( invmas.category = 'uhp' OR invmas.category = 'tour' OR invmas.category = 'mass' OR invmas.category = 'suv' OR invmas.category = 'ltat' OR 
                    invmas.category = 'ltmt' OR invmas.category = 'lthwy' OR invmas.category = 'snow' OR invmas.category = 'stdls' ) THEN od.qty_shipped ELSE NULL END,
    hasCar= CASE WHEN (oh.veh_make != '' AND od.item# !='') THEN 1 ELSE 0 END
    FROM
        comm.dbo.ordetail as od
        LEFT JOIN comm.dbo.invmas as invmas ON invmas.item_num = od.item# --FOR currentCarcount
        INNER JOIN comm.dbo.ordhrd as oh ON oh.location = od.location AND oh.inv_date = od.inv_date AND oh.invoice# = od.invoice#
    WHERE
        od.inv_date BETWEEN '2017-02-01 00:00:00.000' AND '2017-02-28 00:00:00.000'
        AND
        oh.type_of_rec IN('01','02')
)AS DetailList
GROUP BY
    DetailList.location

【讨论】:

  • 我必须进行一些更改才能使其正常工作。两个相同的CASE WHEN 是我的错误。我修好了那些。数据集已完成,运行时间不到 1 秒。非常感谢您花时间为我提供答案。
  • 很高兴我能帮上忙。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多