DISTINCT
导学
DISTINCT 是 SQL 中最简单的去重机制。但它和 GROUP BY 的去重有什么区别?DISTINCT 可以用在聚合函数内部吗?本节把这些问题讲清楚。
定义
DISTINCT:用于从结果集中移除重复行,只保留唯一的值组合。DISTINCT 不是函数,而是作用于 SELECT 列表中所有列的组合。
核心语法
SELECT DISTINCT 列1, 列2 FROM 表;
SELECT COUNT(DISTINCT 列) FROM 表;
完整示例准备:建表与数据
CREATE TABLE employees (
emp_id INT PRIMARY KEY AUTO_INCREMENT,
emp_name VARCHAR(20),
dept VARCHAR(20),
score DECIMAL(5,2)
);
INSERT INTO employees (emp_name, dept, score) VALUES
('大翔', '技术部', 100),
('白歌', '技术部', NULL);
完整示例一:单列去重
当前数据状态
SELECT * FROM employees;
| emp_id | emp_name | dept | score |
|---|---|---|---|
| 1 | 大翔 | 技术部 | 100 |
| 2 | 白歌 | 技术部 | NULL |
执行单列去重
SELECT DISTINCT dept FROM employees;
操作后的结果
| dept |
|---|
| 技术部 |
结果解读
DISTINCT dept对 dept 列去重,只保留唯一的值- 原表有 2 行,去重后只剩 1 个不同的部门
完整示例二:多列组合去重
当前数据状态
基于上面的 employees 表。
执行多列去重
SELECT DISTINCT dept, emp_name FROM employees;
操作后的结果
| dept | emp_name |
|---|---|
| 技术部 | 大翔 |
| 技术部 | 白歌 |
结果解读
DISTINCT作用于dept和emp_name的组合- 只有两列值都相同的行才会被去重
- 原表 2 行,去重后剩 2 种(部门, 姓名)组合
完整示例三:聚合函数中的 DISTINCT
当前数据状态
基于上面的 employees 表。
统计不同部门数量
SELECT COUNT(DISTINCT dept) AS 部门数量 FROM employees;
操作后的结果
| 部门数量 |
|---|
| 1 |
统计不同(部门, 姓名)组合数量
SELECT COUNT(DISTINCT dept, emp_name) AS 组合数量 FROM employees;
操作后的结果
| 组合数量 |
|---|
| 2 |
结果解读
COUNT(DISTINCT 列)先对列去重,再计数- 这是统计唯一值数量的标准写法
COUNT(DISTINCT dept, emp_name)统计两列组合的唯一值数量
完整示例四:DISTINCT 与 ORDER BY 配合
当前数据状态
基于上面的 employees 表。
执行查询
SELECT DISTINCT dept FROM employees ORDER BY dept DESC;
操作后的结果
| dept |
|---|
| 技术部 |
结果解读
DISTINCT可以与ORDER BY配合使用- 先对结果去重,再按 dept 降序排列
完整示例五:DISTINCT 的常见错误用法
当前数据状态
基于上面的 employees 表。
错误写法演示
-- 错误:DISTINCT 只对第一个列去重是误解
SELECT DISTINCT(emp_name), dept FROM employees;
操作后的结果
这个查询实际上是 SELECT DISTINCT emp_name, dept FROM employees,对 emp_name 和 dept 的组合去重。由于 emp_name 每个人都是唯一的,因此返回所有 2 行:
| emp_name | dept |
|---|---|
| 大翔 | 技术部 |
| 白歌 | 技术部 |
结果解读
DISTINCT不是函数,它作用于SELECT列表中所有列的组合DISTINCT(emp_name)这种写法是语法允许的,但括号只是改变了优先级,不改变语义- 很多人认为
DISTINCT(emp_name), dept只对 emp_name 去重,这是错误的理解
完整示例六:DISTINCT vs GROUP BY(无聚合函数时)
当前数据状态
基于上面的 employees 表。
DISTINCT 写法
SELECT DISTINCT dept FROM employees;
| dept |
|---|
| 技术部 |
GROUP BY 写法
SELECT dept FROM employees GROUP BY dept;
| dept |
|---|
| 技术部 |
结果解读
- 无聚合函数时,两条语句结果相同
DISTINCT语义是去重,GROUP BY语义是分组- MySQL 5.7 中,无聚合的
GROUP BY和DISTINCT通常会被优化器处理为相同的执行计划
完整示例七:DISTINCT 不能配合聚合函数(除了嵌套在 COUNT 中)
当前数据状态
基于上面的 employees 表。
错误写法
-- 错误:DISTINCT 不能这样用在 SUM 中(语义不明确)
SELECT SUM(DISTINCT score) FROM employees;
正确写法对比
-- DISTINCT 在 COUNT 中是标准用法
SELECT COUNT(DISTINCT dept) FROM employees;
| COUNT(DISTINCT dept) |
|---|
| 1 |
-- SUM(DISTINCT) 语法上允许,但极少使用
SELECT SUM(DISTINCT score) FROM employees;
| SUM(DISTINCT score) |
|---|
| 100.00 |
结果解读
COUNT(DISTINCT 列)是最常用的 DISTINCT 聚合用法SUM(DISTINCT score)语法上合法,但业务意义通常不明确,极少使用- 原表 score 为 100 和 NULL,DISTINCT 后只有 100 参与求和
AVG(DISTINCT)同理,语法合法但使用场景很少
完整示例八:DISTINCT 不能用于 WHERE
当前数据状态
基于上面的 employees 表。
错误写法
-- 错误:DISTINCT 不能用于 WHERE
SELECT * FROM employees WHERE DISTINCT dept = '技术部';
报错信息
ERROR 1064 (42000): You have an error in your SQL syntax
正确写法
SELECT * FROM employees WHERE dept = '技术部';
操作后的结果
| emp_id | emp_name | dept | score |
|---|---|---|---|
| 1 | 大翔 | 技术部 | 100 |
| 2 | 白歌 | 技术部 | NULL |
结果解读
DISTINCT只能用于SELECT列表,不能用于WHEREWHERE中如需去重逻辑,应考虑使用EXISTS或IN配合子查询
DISTINCT vs GROUP BY
| 特性 | DISTINCT | GROUP BY |
|---|---|---|
| 目的 | 去重 | 分组(可配合聚合) |
| 可用聚合函数 | 不能(除 COUNT(DISTINCT) 外) | 可以 |
| 语义清晰度 | 去重意图明确 | 分组意图明确 |
| 性能 | 两者通常被优化为相同执行计划 |
MySQL 5.7 中,无聚合的 GROUP BY 和 DISTINCT 通常会被优化器处理为相同的执行计划。
常见误区
| 误区 | 正解 |
|---|---|
DISTINCT 是函数 | DISTINCT 是关键字,作用于所有选定列的组合,不是函数。 |
SELECT DISTINCT(col1), col2 只对 col1 去重 | 错误写法。DISTINCT 作用于所有列,col2 的不同值也会保留。 |
DISTINCT 可以用在 WHERE 中 | 不可以。DISTINCT 只能用于 SELECT 列表。 |
面试考点
Q:DISTINCT 和 GROUP BY 去重有什么区别?
无聚合函数时,两者结果相同。
DISTINCT语义是去重,GROUP BY语义是分组。GROUP BY可配合聚合函数使用,DISTINCT不能。
Q:COUNT(*) 和 COUNT(DISTINCT 列) 的区别?
COUNT(*)统计所有行;COUNT(DISTINCT 列)先对该列去重,再统计唯一值的数量。
Q:以下 SQL 的结果是什么?
SELECT COUNT(DISTINCT dept), COUNT(DISTINCT emp_name) FROM employees;返回两列:第一列是不同的部门数量(1),第二列是不同的姓名数量(2)。
小结
DISTINCT对 SELECT 列表的所有列组合去重COUNT(DISTINCT 列)统计唯一值数量- 无聚合时,
DISTINCT和GROUP BY结果相同,但语义不同 DISTINCT不能用于 WHERE,也不能配合聚合函数(除了嵌套在 COUNT 中)
下一章引子:查询语句掌握得差不多了,接下来进入数据库的"结构工程"——如何定义和修改表结构,以及如何设计高效的索引。