group by 不是去重,而是分组;必须配合聚合函数使用,非聚合字段须出现在 group by 中;需去重统计时用 count(distinct) 或子查询;取每组一行应使用窗口函数而非 group by。

GROUP BY 本身不等于去重,它只分组
直接用 GROUP BY 查询多列却不加聚合函数,看起来像“去重”,但本质是按字段组合分组后各取一行——这在 MySQL 5.7 以前可能允许(依赖 sql_mode),但在严格模式或 PostgreSQL/SQL Server 中会报错:column "xxx" must appear in the GROUP BY clause or be used in an aggregate function。所以别把它当 DISTINCT 的替代品来用,尤其当你只想要唯一值、不关心统计时。
真正需要“去重+统计”时,GROUP BY 必须配聚合函数
比如查每个用户最近一次登录时间、每个商品的最高售价、每类订单的平均金额——这些都得靠分组 + 聚合实现:
-
COUNT(*)统计每组行数(如每个城市有多少用户) -
MAX(created_at)取每组最新时间(如每个用户的最后登录时间) -
SUM(DISTINCT amount)对某列去重后再求和(注意:不是对整行去重) -
STRING_AGG(DISTINCT tag, ',')(PostgreSQL)或GROUP_CONCAT(DISTINCT tag)(MySQL)合并去重后的标签
关键点:SELECT 列中,所有非聚合字段必须出现在 GROUP BY 子句里;否则语法不合法。
DISTINCT 和 GROUP BY 混用的典型陷阱
很多人想“先去重再分组”,比如表里有重复的 (user_id, product_id) 记录,想统计每个用户买了几种不同商品。这时不能写:
SELECT user_id, COUNT(product_id) FROM orders GROUP BY user_id;
因为重复记录会让 COUNT 算错。正确做法是:
- 用子查询先去重:
SELECT user_id, COUNT(*) FROM (SELECT DISTINCT user_id, product_id FROM orders) t GROUP BY user_id; - 或直接用
COUNT(DISTINCT product_id)(MySQL/PostgreSQL/SQL Server 2019+ 支持):SELECT user_id, COUNT(DISTINCT product_id) FROM orders GROUP BY user_id;
注意:COUNT(DISTINCT ...) 在大数据量下性能较差,且部分旧版数据库(如 MySQL 5.6)不支持多列去重。
想只取每组一条记录?别硬套 GROUP BY
比如“每个部门工资最高的员工姓名”,GROUP BY dept 配 MAX(salary) 只能拿到最高工资数值,拿不到对应的人名。这时候应该用窗口函数:
SELECT dept, name, salary FROM (SELECT dept, name, salary, ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) rn FROM employees) t WHERE rn = 1;
强行用 GROUP BY + 非聚合字段(如 SELECT dept, name, MAX(salary))在多数数据库里会报错,或返回不可预测的 name 值——这是 SQL 标准明确禁止的行为,不是 bug,是设计如此。











