GREATEST用于横向比较同一行多列取最大值,MAX用于纵向聚合一列所有行取最大值;二者功能、语法、使用场景及性能均不同,不可互换。

SQL里找最大值,GREATEST和MAX根本不是一回事
GREATEST是逐行比较多个列或表达式的值,返回该行里的最大者;MAX是聚合函数,扫描整列数据后返回一个全局最大值。用错场景会直接报错或结果完全不对——比如在WHERE子句里误用MAX而没加GROUP BY,就会提示“invalid use of aggregate function”。
常见错误现象:SELECT id, GREATEST(a, b, c) FROM t WHERE GREATEST(a, b, c) > MAX(d) —— 这条语句在多数数据库(如PostgreSQL、MySQL 8.0+)里会报错,因为MAX(d)是聚合,不能和非聚合表达式混用在同一层级。
-
GREATEST必须传入至少两个同类型(或可隐式转换)的标量表达式,不接受列名列表或子查询 -
MAX只接受单个列或表达式,且必须配合GROUP BY(除非整条查询无其他非聚合字段) - MySQL中
GREATEST(NULL, 1)返回NULL;PostgreSQL里同样逻辑,但Oracle用GREASTEST需注意NULL传播规则
什么时候该用GREATEST:横向比同一行的几个字段
典型场景是清洗数据时取“生效时间”:比如created_at、updated_at、imported_at三者中最新那个作为记录的时间戳。
SELECT id,
GREATEST(created_at, updated_at, imported_at) AS effective_time
FROM logs;注意:GREATEST对NULL敏感——只要任一参数为NULL,整行结果就是NULL。如果想跳过NULL,MySQL可用COALESCE兜底,PostgreSQL可用LEAST/GREATEST配合NULLIF或COALESCE组合:
GREATEST(COALESCE(a, '1970-01-01'), COALESCE(b, '1970-01-01'))- 避免写
GREATEST(a, b, c, NULL)——这等于白写,结果恒为NULL - PostgreSQL 14+ 支持
IGNORE NULLS语法,但仅限于LEAD/LAG等窗口函数,GREATEST仍不支持
什么时候必须用MAX:纵向扫一整列找峰值
比如查某用户最近一次登录时间、某商品历史最高售价、某API调用延迟的P99值(配合PERCENTILE_CONT)——这些都得靠聚合,GREATEST完全无能为力。
性能差异明显:MAX(col)走索引(如果col有B-tree索引)可以秒出;而GREATEST(a,b,c)哪怕所有字段都有索引,数据库也无法利用它们加速逐行计算。
- 在
SELECT里单独用MAX(无GROUP BY),返回的是全表聚合结果,整张表只出一行 - 想按用户分组找各自最大值?必须写
GROUP BY user_id,否则MySQL 5.7严格模式直接报错,PostgreSQL也拒绝执行 -
MAX忽略NULL值,这是它和GREATEST另一个关键区别:即使整列都是NULL,MAX返回NULL;但GREATEST(NULL,NULL)也是NULL,只是原因不同
LEAST不是MIN的替代品,用法镜像但不可互换
LEAST和GREATEST是一对,都是行级比较;MIN和MAX是一对,都是列级聚合。别试图用LEAST代替MIN求最小订单金额——它只能帮你算出每笔订单里“运费、税费、折扣”三者谁最低。
容易踩的坑:有人看到LEAST(a, b)就以为能替代WHERE price = (SELECT MIN(price) FROM items),结果发现查出来一堆记录,因为LEAST根本没扫描全表。
-
LEAST(10, 5, 20)→5;MIN(price)→ 全表最小的那个price值 - SQL标准里
LEAST/GREATEST不是强制实现,SQLite默认不支持,需要编译时开启ENABLE_MATH_FUNCTIONS - 在WHERE条件中嵌套
GREATEST或LEAST可能让优化器放弃使用索引,尤其当参数含函数调用(如GREATEST(updated_at, NOW() - INTERVAL '7 days'))
真正麻烦的是混合场景:既要行内取最大,又要跨行聚合统计。这时候得拆成子查询或CTE,别硬塞在一个SELECT里——否则要么语义混乱,要么执行计划崩掉。










