用窗口函数 row_number() 排序取 top 1 最可靠,需写成子查询或 cte:row_number() over (partition by product_id order by sales desc) as rn,再 where rn = 1;group by + max 或 order by + limit 1 均无法准确返回对应完整记录。

用窗口函数 ROW_NUMBER() 排序取 Top 1 最可靠
直接用 GROUP BY + MAX(sales) 只能拿到最高销量数值,拿不到对应那条完整记录(比如哪个门店、哪天卖的)。真正要“查出销量最高的那条记录”,必须给每组数据排序后筛选。最稳的方式是用窗口函数:ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY sales DESC),然后外层过滤 rn = 1。
注意:如果同一商品有多个记录销量并列最高,ROW_NUMBER() 只会任选其一(因为严格按顺序编号);若需返回全部并列记录,得换用 RANK() 或 DENSE_RANK()。
示例:
SELECT product_id, store_id, sale_date, sales
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY sales DESC) AS rn
FROM sales_table
) t
WHERE rn = 1;
GROUP BY 配合子查询容易漏掉关联字段
有人会先用子查询算出每个商品的最高销量,再跟原表 JOIN 匹配。这方法可行,但极易出错——尤其当原表存在多条同销量记录时,JOIN 条件若只写 t1.product_id = t2.product_id AND t1.sales = t2.max_sales,会把所有并列记录都拉出来,而你可能只想要一条。
更麻烦的是,如果想同时查出 store_id 或 sale_date,这些字段必须出现在子查询的 GROUP BY 里,否则多数数据库(如 MySQL 5.7+ 严格模式、PostgreSQL)会报错:column "store_id" must appear in the GROUP BY clause。
所以除非你明确需要全部并列结果,否则不推荐这种写法。它逻辑绕、可读性差、还容易因分组粒度不对导致结果膨胀。
MySQL 8.0+ 和 PostgreSQL 支持窗口函数,旧版 MySQL 要绕路
MySQL 5.7 及更早版本不支持窗口函数,此时只能用自连接或相关子查询模拟排序。例如:
SELECT s1.product_id, s1.store_id, s1.sale_date, s1.sales FROM sales_table s1 WHERE s1.sales = ( SELECT MAX(s2.sales) FROM sales_table s2 WHERE s2.product_id = s1.product_id );
但这个写法有隐患:
- 如果
sales字段为NULL,MAX()返回NULL,整条记录会被过滤掉 - 性能较差,尤其数据量大时,子查询对每行都执行一次
- 依然无法控制“只取一条”,并列时全返回
所以如果你还在用老版本 MySQL,优先考虑升级,或在应用层做去重。
ORDER BY + LIMIT 1 在分组场景下完全不适用
新手常误写:SELECT * FROM sales_table GROUP BY product_id ORDER BY sales DESC LIMIT 1。这是错的——GROUP BY 后未聚合的字段(如 store_id)值是未定义的,MySQL 可能随机返回某一行,PostgreSQL 则直接报错:column "store_id" must appear in the GROUP BY clause or be used in an aggregate function。
即便侥幸跑通,结果也不具备确定性,上线后可能突然变化,排查困难。
真正要注意的其实是“分组内排序”和“全局排序”的区别。只要需求是“每个商品各取一条销量最高的记录”,就一定得在分组内部完成排序动作,而不是靠外层 ORDER BY。











