需用ROW_NUMBER()在分类内按库存升序编号再筛选:SELECT category, name, stock FROM (SELECT category, name, stock, ROW_NUMBER() OVER (PARTITION BY category ORDER BY stock ASC) AS rn FROM products) ranked WHERE rn <= 5。

用窗口函数 ROW_NUMBER() 按分类分组排序
直接 GROUP BY category 然后 MIN(stock) 只能拿到最小值,拿不到对应商品。必须保留每条商品记录,再在每个分类内按 stock 升序编号。关键不是聚合,是“分组内排序+筛选”。
实操建议:
- 用
ROW_NUMBER() OVER (PARTITION BY category ORDER BY stock ASC)生成序号,注意ASC(升序)才能让库存最少的排前面 - 别用
RANK()或DENSE_RANK():当多个商品库存相同时,它们会得到相同排名,可能突破 Top 5 数量;ROW_NUMBER()保证严格取前 5 条 - 如果分类数量少、数据量不大,子查询或 CTE 都可读性更好,避免嵌套过深
MySQL 8.0+ 和 PostgreSQL 写法基本一致
主流新版数据库都支持标准窗口函数,语法差异极小。但要注意 MySQL 5.7 及更早版本不支持,强行用会报错 ERROR 1064。
示例(通用写法):
SELECT category, name, stock
FROM (
SELECT category, name, stock,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY stock ASC) AS rn
FROM products
) ranked
WHERE rn <= 5;
注意:products 表名、category/name/stock 字段名需按实际调整;rn 是别名,不能在外部 WHERE 中直接写 ROW_NUMBER()...
SQL Server 需确认兼容级别 ≥ 120
SQL Server 2012+ 支持窗口函数,但若数据库兼容级别设为 110(对应 SQL Server 2012 之前),ROW_NUMBER() 在 OVER 中使用 PARTITION BY 会失败,报错 Incorrect syntax near 'ORDER'。
检查并升级(如必要):
- 查当前级别:
SELECT compatibility_level FROM sys.databases WHERE name = DB_NAME(); - 升级命令(需 db_owner 权限):
ALTER DATABASE [your_db] SET COMPATIBILITY_LEVEL = 150; - 低版本绕行方案(不推荐):用
CROSS APPLY+TOP 5,性能差、语义复杂,易出错
有并列库存时要不要保留全部?
如果同一分类里第 5 名和第 6 名库存相同,业务上是否允许“并列入围”?这是容易被跳过的业务逻辑点。
ROW_NUMBER() 会强制截断到 5 条,而 RANK() 会把并列者全算进来(可能返回 6 条或更多)。没有银弹——得看需求:
- 要严格 Top 5 条 → 用
ROW_NUMBER() - 要所有库存 ≤ 第 5 小值的商品 → 用
RANK(),然后WHERE rk - 用
MIN(stock)先算出各分类第 5 小值再 JOIN,更啰嗦但可控性最强,适合审计场景
实际跑出来结果不对,先盯住这一条:到底是“取前 5 行”,还是“取库存值最小的前 N 个不同数值”——后者根本不是 Top 5,而是去重后找最小值。

















