8 DISTINCT
DISTINCT 去除结果集中的重复行。去重时比较的是 SELECT 输出的整行:DISTINCT a, b 的含义是“(a, b) 的非重复组合”,而不是“非重复的 a,再随便取个 b”。PostgreSQL 还提供了它更强大的兄弟 DISTINCT ON (expr),为 expr 的每个非重复值保留一行,很适合“每个 Y 对应的最新 X”这类查询。
本章要点
- 用
DISTINCT去除重复项 DISTINCT如何作用于 SELECT 选择列表中的所有列- PostgreSQL 特有的
DISTINCT ON (expr) DISTINCT如何处理 NULL- 何时应该改用
GROUP BY
示例
在我们的 fruits 表中,每个名称本来就不重复,因此 DISTINCT name 与 SELECT name 返回相同的行。有多列时,只有列出的值全部相同,DISTINCT 才会把两行视为重复行。
要查询“每组的第一行”,DISTINCT ON (substring(name, 1, 1)) 是 PostgreSQL 的拿手好戏。ON (...) 中的表达式定义分组,ORDER BY 决定各组保留哪一行,也就是排序后的第一行。关键在于,ON 中的表达式必须出现在 ORDER BY 的开头,否则 PostgreSQL 无法保证你说的“第一行”是哪一行。
处理空值时,DISTINCT 把两个 NULL 视为相等。考虑到 NULL = NULL 的结果是未知,这有些违反直觉,但 SQL 标准就是这样规定的。
运行示例
运行方式:
常见陷阱
SELECT DISTINCT a, b FROM t与SELECT DISTINCT a, (b) FROM t含义相同,都是对(a, b)组合去重。给b加上括号,并不会让去重只作用于a。DISTINCT与ORDER BY会相互影响:ORDER BY中的任何列如果不在DISTINCT列表中,就会报错(不能按一个在同一去重分组内可能有多个值的列来排序)。DISTINCT ON不是标准 SQL。需要可移植性的代码应改用窗口函数(ROW_NUMBER() OVER (PARTITION BY ...)),后面的章节会介绍。- 在大表上使用
SELECT DISTINCT必须做排序或哈希;如果只想知道“是否有重复项”,可用GROUP BY ... HAVING COUNT(*) > 1或聚合。
count(DISTINCT col) 统计 col 中互不重复的非空值数量。因为要去重,它比 count(*) 慢,但无需子查询就能回答“有多少种不同的 X”。
再插入一行,名称使用已有的值(例如 INSERT INTO fruits (id, name) VALUES (10, 'apple');),然后运行 SELECT DISTINCT name FROM fruits ORDER BY name;,返回的行数应该不变。接着用 DISTINCT ON (name) 保留每个名称中 id 最大的行(提示:ORDER BY name, id DESC)。
下载
SQL 文件: distinct.sql · 全部示例文件 · 准备工作