直接用 MAX(price) 无法获取对应 product_name,因非聚合字段未在 GROUP BY 中或未被聚合函数包裹,数据库拒绝执行;正确做法是用子查询、窗口函数(如 ROW_NUMBER())或 JOIN 获取完整行。

为什么直接用 MAX(price) 只能拿到价格,拿不到对应产品名
这是最常踩的坑:很多人写 SELECT category, MAX(price), product_name FROM products GROUP BY category,结果报错或数据错乱。因为 product_name 不在 GROUP BY 里,也不被聚合函数包裹,数据库(如 MySQL 5.7+、PostgreSQL、SQL Server)会拒绝执行——它不知道你想要哪个 product_name(同一分类下可能有多个产品,但 MAX(price) 只返回一个值)。
正确做法:用子查询先找出每类最高价,再关联原表取完整行
核心思路是分两步:先算出每个 category 对应的最高 price,再用这个结果去匹配原表中“价格等于该最高价”的那条记录。注意要处理同价多产品的情况(比如两个产品都是 999 元且同属 “手机” 类),是否需要全部返回取决于业务需求。
- 如果只要一条(任意一条),可用
ROW_NUMBER()窗口函数(推荐,兼容性好):SELECT category, product_name, price FROM ( SELECT category, product_name, price, ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) AS rn FROM products ) ranked WHERE rn = 1; - 如果要返回所有并列最高价的产品,改用
RANK()或DENSE_RANK():SELECT category, product_name, price FROM ( SELECT category, product_name, price, RANK() OVER (PARTITION BY category ORDER BY price DESC) AS rnk FROM products ) ranked WHERE rnk = 1; - 纯
GROUP BY + JOIN写法(无窗口函数时可用,但较冗长):SELECT p1.category, p1.product_name, p1.price FROM products p1 INNER JOIN ( SELECT category, MAX(price) AS max_price FROM products GROUP BY category ) p2 ON p1.category = p2.category AND p1.price = p2.max_price;
MySQL 8.0+ 和 PostgreSQL 支持 MAX() OVER(),但不能直接替代 GROUP BY
MAX(price) OVER (PARTITION BY category) 是窗口函数,它不会压缩行数,而是给每行都加上本分类的最高价,适合做“标注”而非“筛选”。想只留最高价那条,仍需配合 WHERE 或子查询。误以为加了 OVER 就能省掉 GROUP BY 是常见误解。
- 它返回的是全量行数,不是每类一行
- 无法单独用它取出
product_name,因为窗口函数不改变行粒度 - 若强行
SELECT DISTINCT category, MAX(price) OVER (...) ...,product_name依然不可控
性能和索引建议:给 (category, price) 建联合索引
无论用窗口函数还是子查询 + JOIN,数据库都需要按分类查最大价格。如果表很大,没索引会导致全表扫描。联合索引 (category, price) 能让 GROUP BY category 和 ORDER BY price DESC 都走索引,显著提速。
- 不要只建
category单字段索引——排序price仍需回表或文件排序 - 如果常按
price倒序查,把price放索引第二位并声明DESC(如 PostgreSQL 支持,MySQL 8.0+ 对DESC的优化有限,但联合索引本身已足够) - 测试时用
EXPLAIN看是否用了索引,尤其关注type是否为range或index,避免ALL

















