因为distinct是对整行去重,而非按某列逻辑去重;若select列表含主键、时间戳等高区分度字段,整行几乎永不重复,故无法消除业务意义上的重复——正确做法是精简字段、用窗口函数按组取最新/最旧记录,或改用group by支持后续聚合。

视图里写 DISTINCT 却没去重,为什么?
因为 DISTINCT 是对整行去重,不是按某列“逻辑去重”。只要 SELECT 列表里包含主键、时间戳、自增ID这类高区分度字段,哪怕业务上认为“公司名相同就是重复”,SQL 也永远不会把两行判为重复。
常见错误写法:
CREATE VIEW v_companies AS SELECT DISTINCT company, id, created_at FROM orders;
结果:公司名照样重复。原因:id 和 created_at 几乎总不同,整行永远不重复。
- 想按
company去重?SELECT列表里就不能出现id、created_at等字段 - 视图只是封装查询逻辑,
DISTINCT行为和普通查询完全一致 - 如果必须保留 ID 或时间信息,
DISTINCT就不是正确工具
需要“每组取一条”时,别硬套 DISTINCT
比如:每个 company 只留 created_at 最大的那条记录。这种需求 DISTINCT 根本做不到,它没有“排序+取首行”的能力。
正确做法是用窗口函数,在视图定义中直接实现:
CREATE VIEW latest_company_orders AS
SELECT company, id, created_at
FROM (
SELECT company, id, created_at,
ROW_NUMBER() OVER (PARTITION BY company ORDER BY created_at DESC) AS rn
FROM orders
) t
WHERE rn = 1;
-
ROW_NUMBER()比GROUP BY更可控:能明确指定保留哪条(最新/最旧/最大ID) - 视图外层无需再加聚合,直接
SELECT *就能得到干净结果 - MySQL 8.0+、PostgreSQL、SQL Server 都支持,但 SQLite 不支持窗口函数
DISTINCT 视图 vs GROUP BY 视图,怎么选?
语法上 SELECT DISTINCT a,b FROM t 和 SELECT a,b FROM t GROUP BY a,b 返回结果常一样,但在视图场景下关键差异明显:
-
DISTINCT视图不能在外层再加聚合,比如SELECT *, COUNT(*) FROM v_distinct会报错 -
GROUP BY视图天然可扩展:SELECT a, b, COUNT(*) FROM v_grouped GROUP BY a, b合法且常见 - MySQL 5.7+ 严格模式下,
GROUP BY视图若漏写非分组字段,建视图时就失败;DISTINCT视图更宽松,但容易掩盖语义错误 - 如果后续要统计重复次数,
GROUP BY是唯一自然路径
性能隐患:视图里的 DISTINCT 很可能白跑
视图不执行,但每次查它,DISTINCT 都要真实做全表扫描 + 排序。问题集中在:
- 去重字段没索引(如
SELECT DISTINCT UPPER(name), city),无法走索引,纯靠排序去重 - 嵌套查询时谓词难以下推:比如
SELECT * FROM v_distinct WHERE city = 'Beijing',优化器可能先全量去重再过滤,而不是先过滤再去重 - PostgreSQL 的
DISTINCT ON (col)虽方便,但属非标语法,放进视图会牺牲跨数据库可移植性
真正该警惕的,不是“怎么写”,而是“为什么需要这个视图”——如果业务上反复要求“按某列取最新一条”,说明底层数据模型或写入逻辑可能已偏离预期,视图只是补救,不是根治。











