distinct用于整行去重且无需聚合,更直观安全;group by用于分组聚合,但需确保非聚合字段有明确来源,否则可能报错或结果随机。

视图里用 DISTINCT 还是 GROUP BY?看你要啥结果
直接说结论:如果只是去重,且不需要聚合计算,DISTINCT 更直观、更安全;如果要统计、求和、拼接或分组后取最新/最旧记录,必须用 GROUP BY —— 但得小心字段一致性。
很多人写视图时随手套个 GROUP BY id 就以为去重了,结果发现其他字段值随机(尤其 MySQL 5.7+ 默认 sql_mode 含 ONLY_FULL_GROUP_BY 时直接报错:Expression #2 of SELECT list is not in GROUP BY clause)。
-
DISTINCT是对整行去重,所有列组合相同才合并,不改变原始行结构 -
GROUP BY是按指定列分组,其余非聚合字段必须明确来源(比如MAX(created_at)或ANY_VALUE(name)),否则语法或语义都可能出问题 - 视图定义中用
GROUP BY后,外部再ORDER BY可能失效(部分数据库如 PostgreSQL 要求视图内显式写ORDER BY才生效,但标准 SQL 不保证)
MySQL 视图里 GROUP BY 报 ONLY_FULL_GROUP_BY 错误怎么办
这不是 bug,是 MySQL 在帮你拦住逻辑错误。默认开启的 ONLY_FULL_GROUP_BY 模式要求:所有 SELECT 列要么在 GROUP BY 中,要么是聚合函数结果。
常见错误写法:SELECT user_id, name, created_at FROM logs GROUP BY user_id —— name 和 created_at 没说明取哪一行,数据库无法确定。
- 真要取最新一条:改用
MAX(created_at)配合子查询,或窗口函数(MySQL 8.0+)ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) - 想保留任意一个非聚合值:显式用
ANY_VALUE(name),但得确认业务允许“随机选” - 临时关掉该模式(不推荐):
SET sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));—— 视图创建后仍可能在不同会话中行为不一致
PostgreSQL 视图中用 DISTINCT ON 替代 GROUP BY 的实际场景
当你要“每组取第一条”,又不想写复杂子查询时,DISTINCT ON 比 GROUP BY 更精准、更易读。它不是标准 SQL,但 PostgreSQL 原生支持,且性能通常更好。
比如查每个用户最新一条订单:SELECT DISTINCT ON (user_id) * FROM orders ORDER BY user_id, created_at DESC —— 注意 ORDER BY 必须以 DISTINCT ON 字段开头,否则结果不可靠。
-
DISTINCT ON不需要聚合函数,也不受GROUP BY字段限制,适合“取样”类需求 - 不能和普通
DISTINCT混用;也不能在视图里省略ORDER BY,否则顺序无保证 - 迁移到其他数据库(如 SQL Server、Oracle)时需重写为
ROW_NUMBER()窗口函数
SQL Server 视图中避免重复记录的隐藏陷阱
SQL Server 对视图字段别名、排序、TOP 子句有特殊限制。比如你在视图里写 SELECT DISTINCT *,但如果底层表有计算列或 GETDATE() 这类非确定性函数,每次查询结果都可能不同,导致视图“看似去重实则不稳定”。
另一个典型坑:UNION 自动去重,但 UNION ALL 不去——有人为性能改用 UNION ALL,却忘了上游数据本身就有重复,结果视图输出翻倍。
- 视图定义中尽量避免
SELECT *,尤其是跨多表JOIN后,字段重复或隐式类型转换可能导致DISTINCT失效 - 如果依赖
ROW_NUMBER()去重,记得加上确定性ORDER BY(比如主键),否则同一查询多次执行可能返回不同行 - SQL Server 2016+ 支持
STRING_AGG(),但若在视图中用于拼接去重后的标签,要注意空值处理:STRING_AGG(ISNULL(tag, ''), ',')
视图里的重复处理从来不是纯语法问题,而是你对“哪一行才算重复”“哪一列决定唯一性”的理解是否落地到具体字段和业务规则上。写完务必用真实数据跑一遍,别只看 EXPLAIN。










