SQL 服务器查询以获取出货量排名前 10 的产品、平均值、mtd 和 ytd

SQL Server query to get top 10 products shipped, avg, mtd & ytd

对于库存报告,我需要为最畅销的 10 种产品生成输出

 product     description   in-stock   MTD     YTD     Avg
---------------------------------------------------------------------------
P1 (altp1)  product X     100         30     1000    33
P2 (altp2)  product Y      10          0      100     9

产品相关表:

订单相关表:

仓库库存

我尝试 SQL 使用连接和子查询,但处理结果花了大约 7 分钟

SELECT TOP 10
    p.sku
    ,p1.alt_sku
    ,p.description
    ,wh.qty
    ,SUM(od.shipped_qty) as YTD
    ,SUM(od.shipped_qty) as MTD
    ,AVG(od.shipped_qty) as AvgSales -- how do I get avg of last six months?
FROM product as p
INNER JOIN product_details as p1 ON p.sku = p1.sku
LEFT JOIN inventory as wh on wh.sku = p.sku
LEFT JOIN order_details as od on od.sku= p.sku
LEFT JOIN orders as o on o.order_id = od.order_id
WHERE o.shipped_date >= @YearStart
GROUP BY p.sku, p1.alt_sku, p.description, o.shipped_date, wh.qty, od.shipped_qty
ORDER by MTD DESC

我尝试使用 CTE,它大大加快了查询速度。但是结果出现了重复的skus和double entries

WITH CTE_Summary(product_id, yy, mm, shipped) as (
    SELECT 
        orders.product_id, yy, mm, SUM(orders.shipped_qty) as shipped
    FROM (
        select o.id
            , od.product_id
            , SUM(od.shipped_qty) shipped_qty
            , year(o.shipped_date) yy
            , month(o.shipped_date) mm
        from orders o
        LEFT JOIN order_details od on od.order_id = o.id
        GROUP BY o.id, od.product_id, year(o.shipped_date), month(o.shipped_date)   
    ) AS orders
    WHERE orders.yy >= YEAR(GetDate())
    GROUP BY orders.product_id, orders.yy, orders.mm
) 
SELECT  sku.sku, sku.description, sku.alt_sku, sku.instock, summ.avgshipped, summ.YTD, summ.MTD, sku.sku, sku.DESCR, sku.ALTSKU, sku.DESCR, sku.instock  
FROM (SELECT top 10 product_id
    , AVG(shipped) as avgshipped
    , SUM(shipped) as YTD
    , SUM(CASE WHEN mm = MONTH(GetDate()) THEN shipped ELSE 0 END) as mtd 
from CTE_Summary GROUP BY product_id order by mtd desc, ytd desc) summ
LEFT JOIN (
    SELECT p1.sku, p1.description, p2.alt_sku, wh.qty as instock FROM products p1 
        LEFT JOIN product_details p2 ON p2.product_id = p1.id
        LEFT JOIN inventory wh on wh.product_id = p1.id
) sku ON sku.product_id = summ.product_id

但是每个产品 returns 多行(类似这样)

 product     description   in-stock   MTD     YTD     Avg
---------------------------------------------------------------------------
P1 (altp1)  product X     100         30     1000    33
P1 (altp1)  product X     100         30     1000    33
P2 (altp2)  product Y      10          0      100     9
P2 (altp2)  product Y      10          0      100     9
P2 (altp2)  product Y      10          0      100     9
P3 (altp3)  product Z      30          0       20     1
...

我做错了什么?

前 10 个产品信息在 order_details table 中,但您需要有订单 table 才能根据 orders.shipped_date 进行筛选。 您需要产品 table 来获取产品相关信息和库存 table 来了解当前库存。

所以我将从 order 和 order_details table inner joined 开始,因为在 order_details table.

中没有使用带有 null 的订单

每个订单详细信息都有产品 sku,所以它应该与产品内部连接' 最后我在考虑所有的产品都是库存项目,当我们寻找最畅销的产品时,这些产品必须记录在库存中,这样库存也会被内联。

不确定为什么要在查询中使用产品详细信息。 虽然您在查询中选择了 alt_sku,但它不在您的结果集中。

如果您不需要此 alt_sku 和按月最畅销的产品,查询将如下所示。

DECLARE @FirstDayOfYear AS DATETIME = DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0);
DECLARE @CurrentMonth AS INTEGER = MONTH(GetDate());

SELECT TOP 10
    p.sku                   as product
    ,p.description          as [description]
    ,SUM(wh.qty)            as [in-stock]
    ,SUM(od.shipped_qty)    as YTD
    ,SUM(CASE WHEN MONTH(o.shipped_date) = @CurrentMonth THEN od.shipped_qty ELSE 0 END)    as MTD
    ,AVG(od.shipped_qty)    as [Avg] 
FROM 
    orders as o
    INNER JOIN order_details    as od   on o.order_id = od.order_id
    INNER JOIN product          as p    on od.sku= p.sku
    INNER JOIN inventory        as wh   on wh.sku = p.sku
WHERE o.shipped_date >= @FirstDayOfYear
GROUP BY p.sku, p.description
ORDER by MTD DESC

我修改了查询,现在看起来是这样的。它在 3 秒内加载完毕!

declare @ReportDate as date = GETDATE();
declare @YearStart as date = DATEFROMPARTS(YEAR(@ReportDate), 1, 1);
declare @MonthStart as date = DATEFROMPARTS(YEAR(@ReportDate), MONTH(@ReportDate), 1);
declare @Last6Months as date = DATEADD(mm, -6, @ReportDate)

WITH CTE_Summary (product_id, shipdate, shipped) AS (
    SELECT product_id, orders.shipped_date, order_details.shipped_qty FROM order_details 
        LEFT JOIN orders ON orders.order_id = order_details.order_id 
        WHERE orders.shipped_date BETWEEN IIF(@YearStart > @Last6Months, @Last6Months, @YearStart) AND @ReportDate
            AND orders.shipdate IS NOT NULL
)
SELECT TOP 10
        o.product_id 
        , ytd = (SELECT SUM(shipped) FROM CTE_Summary y where y.sku = o.sku AND y.shipdate >= @YearStart)
        , mtd = (SELECT SUM(shipped) FROM CTE_Summary m where m.sku = o.sku AND m.shipdate >= @MonthStart)
        , [avg] = (SELECT AVG(shipped) FROM CTE_Summary a where a.sku = o.sku AND a.shipdate>= @Last6Months)
        , stock = (SELECT qty FROM inventory wh where wh.sku = o.sku)
FROM CTE_Summary o
GROUP BY o.sku
order by ytd desc