标准SQL不支持MAX BY或MIN BY语法;ClickHouse使用argMax/argMin实现等效功能,如argMax(value_col, sort_col)返回sort_col最大时对应的value_col值。

SQL里根本没有MAX BY或MIN BY函数
直接说结论:标准SQL(包括PostgreSQL、MySQL、SQL Server、SQLite)均不支持MAX BY或MIN BY语法。这是常见误解,往往源于把ClickHouse或Doris等分析型数据库的扩展语法当成了通用能力。如果你在非ClickHouse环境里写了MAX(column1) BY column2,大概率会报错:syntax error near BY 或类似提示。
ClickHouse中argMax/argMin才是正解
ClickHouse提供了argMax和argMin聚合函数,这才是真正实现“取某列最大值时,连带返回另一列对应值”的标准方式。它不是MAX BY,但语义等价:
-
argMax(value_col, sort_col):按sort_col升序排,取value_col对应的最大sort_col那一行的值</li> <li><code>argMin(value_col, sort_col)
:同理,取sort_col最小值对应行的value_col - 注意:两个参数顺序不能颠倒——第二参数是用于比较的“排序键”,第一参数才是你要取的“伴随值”
示例:查每个user_id下单时间最晚的订单号
SELECT user_id, argMax(order_id, created_at) AS latest_order_id FROM orders GROUP BY user_id
其他数据库得用窗口函数或关联子查询
在PostgreSQL/MySQL 8.0+/SQL Server中,必须组合使用窗口函数或自连接。核心思路是:先标出极值行,再过滤或关联。
- 用
ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ... DESC)标记每组内排序第一的行,然后外层WHERE rn = 1 - 避免用
RANK()或DENSE_RANK()——当存在并列极值时,它们会返回多行,而argMax默认只取一个(通常是第一个遇到的) - MySQL 5.7及更早版本不支持窗口函数,只能用相关子查询,性能较差,且需确保
ORDER BY字段有索引
PostgreSQL示例(取每个部门薪资最高员工的姓名):
SELECT dept, name
FROM (
SELECT dept, name, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM employees
) t
WHERE rn = 1;
别忽略NULL和重复值带来的行为差异
argMax在ClickHouse中遇到sort_col为NULL时,会把该行排在最后(即视为最大值之后),所以NULL不会干扰结果;但其他数据库用ORDER BY ... DESC时,NULL默认排最前(PostgreSQL)或最后(MySQL),行为不一致。
- 显式写
ORDER BY col DESC NULLS LAST可统一控制NULL位置(PostgreSQL支持,MySQL不支持该子句) - 如果
sort_col有重复值(比如多人同薪),argMax返回的是任意一个匹配行的value_col,不保证稳定;若需确定性结果,应在ORDER BY后追加主键或唯一字段作为第二排序条件 - 所有方案都要求
GROUP BY或PARTITION BY字段与业务分组逻辑严格对齐,漏掉一个维度就可能聚合错误
真正麻烦的从来不是语法怎么写,而是搞清“极值”定义本身是否明确——时间戳精度、字符串排序规则、NULL参与比较的方式,这些细节一旦没对齐,结果就不可信。

















