首页/数据/SQL 速查/按表达式或函数分组
聚合与窗口函数54 条

按表达式或函数分组

分组键可以是表达式:按天/月统计是常见需求;表达式分组通常无法直接用普通索引,可考虑生成列或预聚合。

SQL 速查数据更新于 2026-10-13

按表达式分组是什么

GROUP BY 的分组键不限于列,也可以是一段表达式:按天、按月、按函数处理后的结果分组都是常见需求。代价是表达式分组一般用不上普通列索引,要靠改写条件、生成列或表达式索引来救,否则每次统计都得扫全表再排序。

基本信息

支持范围MySQL 8 与 PostgreSQL 都允许分组键是任意表达式
序号简写两库都支持按输出列序号分组,如 GROUP BY 1
索引表达式分组一般无法直接使用普通列索引
优化手段MySQL 8.0.13+ 用表达式索引或生成列;PostgreSQL 建表达式索引
时区按天分组前先统一时区,否则日界会漂移
过滤位置过滤原始行用 WHERE,过滤分组结果用 HAVING

MySQL 按天统计

MySQL 按天统计
SELECT DATE(created_at) AS day,
       COUNT(*)        AS orders,       -- 当天订单数
       SUM(amount)     AS amount_sum    -- 当天金额合计
FROM orders
WHERE created_at >= '2024-01-01'        -- 先按列做范围过滤,保住索引
  AND created_at <  '2024-02-01'
GROUP BY DATE(created_at)               -- 分组键是表达式
ORDER BY day;                           -- 不写 ORDER BY 结果顺序不保证

PostgreSQL 按月与表达式索引

PostgreSQL 按月与表达式索引
SELECT date_trunc('month', created_at) AS mon,
       COUNT(*)                        AS orders
FROM orders
GROUP BY 1                             -- 按输出列序号分组
ORDER BY mon;

-- 建表达式索引,让上面的按月分组与排序能走索引
CREATE INDEX idx_orders_mon ON orders (date_trunc('month', created_at));

常见分组键写法

需求MySQL 8PostgreSQL
按天GROUP BY DATE(col)GROUP BY col::date 或 date_trunc('day', col)
按月GROUP BY DATE_FORMAT(col,'%Y-%m')GROUP BY date_trunc('month', col)
按序号支持 GROUP BY 1支持 GROUP BY 1
函数索引8.0.13+ 表达式索引或生成列CREATE INDEX ... (表达式)
结果顺序分组后顺序不保证同样需要显式 ORDER BY

典型使用场景

  • 按天、按周、按月出趋势报表
  • 按金额区间分桶,统计订单分布
  • 按字符串处理结果分组:域名后缀、手机号号段、去空格后的名称
  • 按业务计算字段分组,如毛利档位、评分区间
  • 预处理宽表:先把常用口径聚合好,上层直接查
  • 数据质量排查:按归一化后的键分组,看是否存在重复主体
GROUP BY 与 ORDER BY 里引用的表达式要完全一致:MySQL 可以按别名排序,PostgreSQL 可以按输出列序号分组,跨库脚本写明完整表达式比依赖简写更稳妥。
WHERE 里对列做函数会让索引用不上:WHERE DATE(created_at) = '2024-06-01' 改成范围条件 created_at >= '2024-06-01' AND created_at < '2024-06-02' 才能走索引。

易错点

  • WHERE 里用函数包裹索引列:索引失效退化为全表扫描,改写为对常量一侧做运算的范围条件
  • 跨时区按天分组:日界随服务器时区漂移,先统一到业务时区再分组
  • GROUP BY 与 ORDER BY 表达式不一致:排序口径与分组口径对不上,结果看似正常实则错位
  • 忘了显式排序:分组结果顺序不保证,报表直接取首行可能取到随机分组
  • 为每个新口径都加表达式索引:索引数量与写入放大会同步上升,按查询频率决定是否值得

数据来源与参考

  • MySQL 8.0 Reference Manual · GROUP BY
  • PostgreSQL Documentation · GROUP BY
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌