You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

每个类别销量Top3产品查询的SQL错误分析及优化方案咨询

问题分析与优化方案

咱们先聊聊你的SQL语句为啥没得到预期结果,再给你推荐个更高效的实现方式~

你的SQL存在的问题

  1. NULL值导致的过滤失效
    你的cat2类别里,prod8prod10没有对应的订单记录,所以它们的sales计算结果是NULL。当你在子查询里取cat2的销量排序后,顺序是10, 1, NULLoffset 2 rows取到的是NULL。而SQL里任何值和NULL比较的结果都是未知(UNKNOWN),会被当作false处理,所以cat2里所有产品都不满足p2.sales >= NULL的条件,自然没出现在结果里。

  2. 性能低下的嵌套子查询
    你嵌套了多层子查询,而且每个产品都单独执行一次sum(quantity)的子查询,相当于对OrderItems表做了N次全表扫描(N是产品总数),数据量越大,性能越差。

  3. 并列排名的处理缺陷
    如果有多个产品销量并列第三,你的写法会漏掉这些并列的产品,因为用了distinctoffset取单一值,没有考虑排名的并列情况。

更优的实现方式:使用窗口函数

在SQL Server(2008及以上版本)中,用窗口函数ROW_NUMBER()DENSE_RANK()是处理分组排名最简洁高效的方案,逻辑清晰且性能更好。

方案1:获取每个类别销量前三(不保留并列排名)

如果只需要每个类别严格的前三(即使有并列,只取其中一个),用ROW_NUMBER()

WITH ProductSales AS (
    -- 先一次性计算所有产品的总销量,LEFT JOIN保证无订单的产品也被统计
    SELECT 
        p.id,
        p.category,
        ISNULL(SUM(oi.quantity), 0) AS sales  -- 把NULL销量转为0,避免排名异常
    FROM Products p
    LEFT JOIN OrderItems oi ON p.id = oi.prodID
    GROUP BY p.id, p.category
),
RankedProducts AS (
    -- 按类别分区,每个类别内按销量降序排名
    SELECT 
        *,
        ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) AS sales_rank
    FROM ProductSales
)
-- 筛选每个类别排名前3的产品
SELECT id, category, sales
FROM RankedProducts
WHERE sales_rank <= 3
ORDER BY category, sales DESC;

方案2:保留并列排名的前三

如果需要保留销量并列的产品(比如两个产品都是第三,都要显示),把ROW_NUMBER()换成DENSE_RANK()即可:

WITH ProductSales AS (
    SELECT 
        p.id,
        p.category,
        ISNULL(SUM(oi.quantity), 0) AS sales
    FROM Products p
    LEFT JOIN OrderItems oi ON p.id = oi.prodID
    GROUP BY p.id, p.category
),
RankedProducts AS (
    SELECT 
        *,
        DENSE_RANK() OVER (PARTITION BY category ORDER BY sales DESC) AS sales_rank
    FROM ProductSales
)
SELECT id, category, sales
FROM RankedProducts
WHERE sales_rank <= 3
ORDER BY category, sales DESC;

运行这个查询,就能得到你预期的结果,同时还能处理无订单产品的销量问题,性能也比你的原SQL好很多。

内容的提问来源于stack exchange,提问作者John L.

火山引擎 最新活动