DISTINCT 与去重
DISTINCT 基础
DISTINCT 用于去除结果集中的重复行。
基本语法:
SELECT DISTINCT column1, column2, ...
FROM table;为什么需要去重
首先创建一个有重复数据的示例表:
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer_name VARCHAR(50),
product VARCHAR(50),
amount DECIMAL(10, 2)
);
INSERT INTO orders (customer_name, product, amount) VALUES
('Alice', 'Laptop', 1200.00),
('Bob', 'Mouse', 25.00),
('Alice', 'Keyboard', 80.00),
('Charlie', 'Monitor', 300.00),
('Bob', 'Headphones', 50.00),
('Alice', 'USB Cable', 10.00);查询所有客户名称:
SELECT customer_name FROM orders;结果有重复:
customer_name
---------------
Alice
Bob
Alice
Charlie
Bob
Alice使用 DISTINCT 去重
SELECT DISTINCT customer_name FROM orders;结果:
customer_name
---------------
Alice
Bob
Charlie每个客户名只出现一次。
多列 DISTINCT
DISTINCT 作用于整行,只有所有选中的列都相同才算重复:
SELECT DISTINCT customer_name, product FROM orders;结果:
customer_name | product
---------------+------------
Alice | Laptop
Bob | Mouse
Alice | Keyboard
Charlie | Monitor
Bob | Headphones
Alice | USB Cable没有去重,因为每行的 (customer_name, product) 组合都是唯一的。
常见误解:DISTINCT 不能只作用于部分列
-- 错误理解:只对 customer_name 去重
SELECT DISTINCT customer_name, product FROM orders;这会对 (customer_name, product) 整体去重,而不是只对 customer_name 去重。
如果想要"每个客户的第一个产品",需要使用窗口函数或子查询(后续章节)。
DISTINCT 与聚合函数
DISTINCT 可以在聚合函数内部使用(下一章详细讲解):
-- 计算不同客户的数量
SELECT COUNT(DISTINCT customer_name) AS unique_customers FROM orders;结果:
unique_customers
------------------
3这与先去重再计数不同:
-- 这会先去重,然后计数去重后的行数
SELECT COUNT(*) FROM (SELECT DISTINCT customer_name FROM orders) AS t;DISTINCT 的执行顺序
DISTINCT 在 SELECT 之后、ORDER BY 之前执行:
执行顺序:FROM → WHERE → SELECT → DISTINCT → ORDER BY → LIMIT
示例:
SELECT DISTINCT customer_name
FROM orders
ORDER BY customer_name;结果:
customer_name
---------------
Alice
Bob
CharlieDISTINCT 与 ORDER BY 的陷阱
ORDER BY 中的列必须在 SELECT 列表中(当使用 DISTINCT 时):
-- 错误:product 不在 SELECT 列表中
SELECT DISTINCT customer_name
FROM orders
ORDER BY product;错误信息(PostgreSQL):
ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list为什么? 因为 DISTINCT 去重后,每个客户可能对应多个产品,数据库不知道按哪个产品排序。
正确写法:
-- 同时选择两列,然后去重
SELECT DISTINCT customer_name, product
FROM orders
ORDER BY customer_name, product;或者只按 SELECT 中的列排序:
SELECT DISTINCT customer_name
FROM orders
ORDER BY customer_name;DISTINCT ON(PostgreSQL 特有)
PostgreSQL 提供 DISTINCT ON 用于更灵活的去重:
-- 每个客户的第一个订单(按 id 排序)
SELECT DISTINCT ON (customer_name)
customer_name, product, amount
FROM orders
ORDER BY customer_name, id;结果:
customer_name | product | amount
---------------+----------+---------
Alice | Laptop | 1200.00
Bob | Mouse | 25.00
Charlie | Monitor | 300.00DISTINCT ON (column) 的语义:
- 对指定列去重
- 保留每组的"第一行"(由 ORDER BY 决定)
MySQL 没有 DISTINCT ON,需要用子查询或窗口函数实现。
DISTINCT 的性能影响
去重需要排序或哈希
数据库通常用两种方式实现 DISTINCT:
- 排序:先排序,然后跳过相邻的重复行
- 哈希:用哈希表记录已见过的行
两种方式都有开销,大数据集上 DISTINCT 可能很慢。
查看执行计划
PostgreSQL:
EXPLAIN SELECT DISTINCT customer_name FROM orders;可能的输出:
Unique (cost=...)
-> Sort (cost=...)
Sort Key: customer_name
-> Seq Scan on orders (cost=...)或:
HashAggregate (cost=...)
Group Key: customer_name
-> Seq Scan on orders (cost=...)性能优化建议
避免不必要的 DISTINCT:先确认数据是否真的有重复。
在列上创建索引:索引可以加速排序。
考虑 GROUP BY:有时 GROUP BY 配合聚合函数更高效(下一章)。
限制选择的列:DISTINCT 作用于所有选中的列,列越多去重越慢。
DISTINCT vs GROUP BY
以下两个查询结果相同:
-- 使用 DISTINCT
SELECT DISTINCT customer_name FROM orders;
-- 使用 GROUP BY
SELECT customer_name FROM orders GROUP BY customer_name;GROUP BY 通常配合聚合函数使用(下一章详细讲解):
-- 每个客户的订单数量
SELECT customer_name, COUNT(*) AS order_count
FROM orders
GROUP BY customer_name;结果:
customer_name | order_count
---------------+-------------
Alice | 3
Bob | 2
Charlie | 1ALL(默认行为)
SELECT 默认返回所有行(包括重复),这等价于 SELECT ALL:
-- 以下两个查询等价
SELECT customer_name FROM orders;
SELECT ALL customer_name FROM orders;ALL 是默认值,很少显式写出。
练习
使用本节创建的 orders 表完成以下练习:
基本去重:查询所有不重复的产品名称。
多列去重:查询所有不重复的
(customer_name, product)组合。统计去重:计算有多少个不同的客户(使用 COUNT DISTINCT)。
去重后排序:查询所有不同的客户名称,按字母顺序排序。
理解差异:解释以下两个查询的区别:
sqlSELECT DISTINCT customer_name FROM orders; SELECT customer_name FROM orders GROUP BY customer_name;DISTINCT ON(PostgreSQL):查询每个客户金额最大的订单(提示:使用 DISTINCT ON 和 ORDER BY)。
性能对比:在一个有 10000 行的表上,比较有无 DISTINCT 的查询性能(使用 EXPLAIN ANALYZE)。
实际场景:从 orders 表中获取至少下过一次单的所有客户名称(去重),并按客户名称排序。
完成练习后,打开本章答案页核对。
常见错误
错误 1:以为 DISTINCT 只作用于第一列
-- 错误理解:只对 customer_name 去重
SELECT DISTINCT customer_name, product FROM orders;
-- 实际:对 (customer_name, product) 整体去重错误 2:DISTINCT 与 ORDER BY 不匹配
-- 错误:ORDER BY 的列不在 SELECT 中
SELECT DISTINCT customer_name FROM orders ORDER BY amount;错误 3:过度使用 DISTINCT
-- 不必要的 DISTINCT(如果 id 是主键,本来就不会重复)
SELECT DISTINCT id, name FROM users;先检查是否真的有重复,避免无谓的性能开销。
本章小结
第一章建立了 SQL 查询的基础:
- 数据库安装与连接
- SELECT 与列选择
- WHERE 过滤条件
- ORDER BY 排序与 LIMIT 分页
- DISTINCT 去重
这些是所有 SQL 查询的基础。第二章将学习聚合函数和分组。