SQL DISTINCT 与 GROUP BY 性能分析

1次阅读

distinct通常比group by快,因其仅去重而不触发聚合逻辑,优化器可用哈希或排序一次性完成;group by即使无聚合函数也需预留分组上下文,常引入aggregate节点及额外开销。

SQL DISTINCT 与 GROUP BY 性能分析

为什么 DISTINCT 比 GROUP BY 快(多数情况下)

因为 DISTINCT 是语义更轻的操作:它只做去重,不触发分组聚合逻辑。数据库优化器通常能用哈希或排序一次性完成去重;而 GROUP BY 默认预留了聚合上下文(哪怕没写 AVG()count()),执行计划里常多出 Aggregate 节点,带来额外开销。

常见错误现象:select DISTINCT col1 FROM tSELECT col1 FROM t GROUP BY col1 返回结果一样,但后者执行时间明显长、CPU 占用高——尤其在大表 + 无索引列上。

  • 使用场景:仅需唯一值列表(如下拉筛选项、枚举去重),别用 GROUP BY
  • 参数差异:两者语法不可互换——GROUP BY 后字段必须出现在 SELECT 列表中(除非用聚合函数),DISTINCT 没这限制
  • 性能影响:postgresql 14+ 对单字段 DISTINCT 会自动优化为 GROUP BY 等价形式,但 mysql 8.0 仍严格区分,建议实测 EXPLAIN ANALYZE

GROUP BY 不加聚合函数时,其实悄悄干了什么

当你写 SELECT col1 FROM t GROUP BY col1,表面看和 DISTINCT 一样,但数据库必须确保“每组只返回一行”。问题在于:哪一行?标准 SQL 要求该列必须是确定性可选的(比如主键或函数依赖列),否则报错;MySQL 默认开启 sql_mode=ONLY_FULL_GROUP_BY 前会随机选一行,PostgreSQL 直接拒绝执行。

容易踩的坑:SELECT id, name FROM users GROUP BY name 在 MySQL 5.7 下可能返回任意 id,且无法预测——这不是 bug,是未定义行为。

  • 使用场景:真需要按某列分组并取关联字段(如最新记录),必须显式用聚合(MAX(id))或窗口函数(ROW_NUMBER() OVER (PARTITION BY name ORDER BY updated_at DESC)
  • 兼容性影响:sqlite 允许非确定性 GROUP BY,但结果不可移植;生产环境应禁用 ONLY_FULL_GROUP_BY 关闭状态
  • 示例:SELECT name, MAX(created_at) FROM users GROUP BY name 安全;SELECT id, name FROM users GROUP BY name 危险

索引对 DISTINCT 和 GROUP BY 的影响完全不同

DISTINCT 能直接受益于覆盖索引:如果查询字段都在索引中(如 CREATE INDEX idx_name ON users(name)),数据库扫索引就能完成去重,避免回表。而 GROUP BY 即使走索引,也常因需要分组中间态而无法跳过排序/哈希阶段。

常见错误现象:给 name 加了索引,DISTINCT name 变快了,但 GROUP BY name 执行计划里仍有 using temporary; Using filesort

  • 性能影响:复合索引 (a, b) 能加速 SELECT DISTINCT a, b,但对 GROUP BY a, b 效果取决于是否含聚合字段
  • 实操建议:用 EXPLAINExtra 字段——出现 Using index for group-by(MySQL)才算真正用上索引优化
  • 注意点:PostgreSQL 中 GROUP BY 可利用索引避免排序,但前提是 ORDER BYGROUP BY 字段一致且方向相同

什么时候 GROUP BY 反而比 DISTINCT 更合适

当你需要分组后进一步计算,或者字段组合本身天然具备函数依赖关系时。GROUP BY 是语义正确的起点,硬套 DISTINCT 会掩盖意图、增加维护风险。

容易被忽略的地方:有些 ORM 或 BI 工具自动生成 GROUP BY(比如拖拽字段后点“去重”),你以为它等价于 DISTINCT,其实底层可能已嵌入隐式聚合逻辑,导致后续加字段时突然报错或结果异常。

  • 使用场景:统计类查询(COUNT(*), AVG(price))、按时间窗口聚合(GROUP BY DATE(created_at))、关联子查询中作为派生表
  • 参数差异:GROUP BY 支持 ROLLUPCUBE 等扩展,DISTINCT 完全不支持
  • 实操提醒:不要为了“看起来简洁”把 GROUP BY x 改成 DISTINCT x——即使当前没聚合函数,业务逻辑可能很快需要加
text=ZqhQzanResources