min() 必须配合 group by 才能按组取最小值,单独使用只返回全局最小值;分组时非聚合字段须在 group by 中显式列出,否则报错或结果不可靠;min() 也适用于字符串和日期类型,按默认排序规则取最小值。

MIN() 必须配合 GROUP BY 才能按组取最小值
单独写 SELECT MIN(price) FROM products 只会返回整张表的全局最小值,不是每组的。想按类别、用户、日期等分组取最小值,GROUP BY 是硬性前提,缺它就跑偏。
常见错误是漏掉 GROUP BY 却期望得到多行结果,这时 MySQL 可能允许执行(开启 sql_mode=only_full_group_by 时直接报错),PostgreSQL 和 SQL Server 则直接拒绝。
- 正确写法:
SELECT category, MIN(price) FROM products GROUP BY category - 如果还想查出对应商品名,不能只加
name到 SELECT 里——SELECT category, name, MIN(price)会报错或返回不可靠数据,因为name不在GROUP BY且没聚合 - 想同时拿到最小价格和对应记录的完整字段,得用窗口函数或子查询,
MIN()本身做不到
GROUP BY 字段必须和 SELECT 中非聚合列完全一致
比如按年月分组统计最低销量,DATE_FORMAT(sale_time, '%Y-%m') 和 YEAR(sale_time), MONTH(sale_time) 看似等价,但后者生成两个字段,GROUP BY 就得写两个,否则语法不匹配。
- 安全写法:把计算逻辑提取到子查询或 CTE 中,再对别名分组,避免表达式重复书写出错
- MySQL 8.0+ 支持
GROUP BY引用 SELECT 别名(如SELECT DATE_FORMAT(t, '%Y') AS y FROM t GROUP BY y),但 PostgreSQL 不支持,跨数据库时慎用 - 注意 NULL 分组:含 NULL 的字段参与
GROUP BY时,所有 NULL 会被归为同一组,MIN()对该组正常计算(NULL 被忽略)
MIN() 对字符串、日期也有效,但行为依赖排序规则
MIN() 不限于数字,对 CHAR、TEXT、DATE 类型同样适用,原理是按该类型的默认排序顺序取“最小”值——也就是字典序最前或时间最早的那个。
- 字符串:
MIN(name)返回字典序最小的姓名,比如 'Alice' 比 'Bob' 小,但大小写敏感性取决于 collation(如utf8mb4_0900_as_cs区分大小写,utf8mb4_0900_ai_ci不区分) - 日期:
MIN(created_at)等价于最早时间,没问题;但若字段是DATETIME且含时分秒,要注意精度是否影响业务语义 - 警惕隐式转换:
MIN('2023-10', '2023-9')在某些场景下可能因字符串比较变成 '2023-10'
性能关键:给 GROUP BY 和 MIN 字段建联合索引
当表很大时,GROUP BY category, MIN(price) 如果没索引,会触发全表扫描 + 文件排序,慢得明显。优化核心是让索引覆盖分组键和被聚合字段。
- 推荐索引:
CREATE INDEX idx_cat_price ON products (category, price)—— 注意顺序:分组字段在前,MIN 字段在后 - 为什么不是
(price, category)?因为 B+ 树索引最左前缀原则,GROUP BY category无法利用以price开头的索引 - 如果查询还带
WHERE条件(如WHERE status = 'active'),索引需把过滤字段前置:(status, category, price)
复杂点在于,真实业务里「每组最小值」往往只是中间步骤,后续还要关联原表取完整记录,这时候光靠 MIN() + GROUP BY 不够,得切换思路用 ROW_NUMBER() 或相关子查询——那个就不在 MIN() 的能力范围内了。











