如何修改SQL查询以获取每个品牌的Top2畅销产品?
获取每个品牌前2款畅销产品的SQL解决方案
嘿,你的原SQL已经算出了每个产品的总销量,但还差一步按品牌筛选Top N的逻辑。下面给你几种适配不同场景的解决方案,都是日常开发里常用的思路:
方案1:用ROW_NUMBER()严格取前2(忽略销量并列)
这个方法会给每个品牌下的产品按销量从高到低分配唯一序号,哪怕有产品销量相同,也只会保留前2个,适合需要严格控制返回数量的场景:
WITH product_sales AS ( SELECT products.*, SUM(orderdetails.quantity) AS sold FROM products RIGHT JOIN orderdetails ON orderdetails.product_id = products.id GROUP BY products.id, products.brand_id ) SELECT * FROM ( SELECT *, -- 按品牌分组,销量降序排,给每个产品打序号 ROW_NUMBER() OVER (PARTITION BY brand_id ORDER BY sold DESC) AS rn FROM product_sales WHERE sold IS NOT NULL -- 可选:过滤掉没有销量的产品 ) ranked_products WHERE rn <= 2;
方案2:用RANK()保留销量并列的产品
如果希望某品牌里销量并列的产品都能被返回(比如有3个产品销量都是第一,都想拿到),就换成RANK()函数,它会给相同销量的产品分配相同的排名:
WITH product_sales AS ( SELECT products.*, SUM(orderdetails.quantity) AS sold FROM products RIGHT JOIN orderdetails ON orderdetails.product_id = products.id GROUP BY products.id, products.brand_id ) SELECT * FROM ( SELECT *, RANK() OVER (PARTITION BY brand_id ORDER BY sold DESC) AS rn FROM product_sales WHERE sold IS NOT NULL ) ranked_products WHERE rn <= 2;
兼容旧版本数据库(无窗口函数支持)
如果你的数据库不支持窗口函数(比如MySQL 5.x),可以用关联子查询实现,不过效率会比窗口函数低一些:
SELECT p.*, SUM(od.quantity) AS sold FROM products p RIGHT JOIN orderdetails od ON od.product_id = p.id WHERE ( SELECT COUNT(DISTINCT SUM(od2.quantity)) FROM products p2 JOIN orderdetails od2 ON od2.product_id = p2.id WHERE p2.brand_id = p.brand_id GROUP BY p2.id HAVING SUM(od2.quantity) >= SUM(od.quantity) ) <= 2 GROUP BY p.id, p.brand_id ORDER BY p.brand_id, sold DESC;
小提醒:
- 原SQL用了
RIGHT JOIN,会包含没有任何订单的产品(销量为NULL),如果不需要这类数据,建议换成INNER JOIN,结果更精准。 - 分组时确保
products.id是主键,避免因数据库GROUP BY模式严格导致的报错。
内容的提问来源于stack exchange,提问作者Omid
相关产品推荐
相关产品推荐

