<p>最简方案是用 MAX() 和 MIN() 直接相减:SELECT category, MAX(price) - MIN(price) AS range_price FROM products GROUP BY category;注意 NULL 处理与 HAVING 误用。</p>

用 MAX() 和 MIN() 直接相减是最简方案
聚合函数本身不支持“组内极差”这种衍生计算,但极差(最大值减最小值)是标量运算,只要在同一 GROUP BY 上下文中把 MAX() 和 MIN() 一起查出来,就能直接相减。不需要子查询或窗口函数。
常见错误是试图在 HAVING 中写 MAX(col) - MIN(col) 来过滤,但 HAVING 只能过滤分组结果,不能生成新字段;正确做法是在 SELECT 列表里算:
SELECT category, MAX(price) - MIN(price) AS range_price FROM products GROUP BY category;
注意:如果某组所有值为 NULL,MAX() 和 MIN() 都返回 NULL,相减结果仍是 NULL,不是 0 —— 这是易忽略点。
遇到 NULL 值时,COALESCE 或 IS NULL 判断不可少
当字段含 NULL 时,MIN()/MAX() 会自动跳过它们,这通常符合预期;但若整组只有 NULL,结果就是 NULL,可能导致后续计算中断(比如除以该极差)。
根据业务需求选择处理方式:
- 把
NULL极差统一视为 0:COALESCE(MAX(val) - MIN(val), 0) - 只保留有有效极差的组:
HAVING COUNT(val) > 1(注意不是COUNT(*),后者会把NULL行也计数) - 明确标记缺失:
CASE WHEN MAX(val) IS NULL THEN 'no_data' ELSE CAST(MAX(val)-MIN(val) AS TEXT) END
想同时拿到极差和对应原始行?得用窗口函数而非聚合
聚合函数会压缩行数,如果你既要每组的极差,又要看到属于该组的每一行(比如标出哪条记录离极值最近),就不能只靠 GROUP BY。
此时应改用窗口函数,在不折叠行的前提下计算组内极值:
SELECT id, category, price,
MAX(price) OVER (PARTITION BY category) - MIN(price) OVER (PARTITION BY category) AS range_price
FROM products;注意两点:
- 窗口函数不改变行数,所以不能和普通聚合混用在同一个
SELECT里(除非外层再套一层查询) -
PARTITION BY必须和你要分组的维度一致,否则极差算错
性能敏感场景下,避免在 WHERE 中对聚合结果做条件
有人会写 WHERE MAX(price) - MIN(price) > 100,这语法错误——聚合函数不能出现在 WHERE 子句中,因为 WHERE 执行在分组前。
必须用 HAVING,但要注意:HAVING 是在聚合后过滤,无法利用索引加速。如果数据量大且过滤条件强,更高效的方式是先用子查询缩小范围:
SELECT category, MAX(price) - MIN(price) AS range_price FROM products WHERE price IS NOT NULL -- 先剔掉明显无效数据 GROUP BY category HAVING MAX(price) - MIN(price) > 100;
真正容易被忽略的是:极差本身不具备可索引性,优化只能从预过滤、分区裁剪或物化中间结果入手。

















