SELECT DISTINCT 是 SQL 中用于去除查询结果重复行的常用语法,适合处理数据去重、分类统计前的数据整理以及唯一值查询等场景。理解 DISTINCT 的作用范围、NULL 处理方式以及与 GROUP BY、COUNT 的区别,可以避免查询结果出现重复数据或统计结果不符合预期的问题。
一、SELECT DISTINCT的基本语法
DISTINCT 通常紧跟在 SELECT 后面,用于要求查询结果中的记录保持唯一。
基本语法如下:
SQLSELECT DISTINCT column_name FROM table_name;
例如,有一张员工表 employees:
id name department 1 张三 技术部 2 李四 销售部 3 王五 技术部 4 赵六 财务部 5 钱七 销售部
如果直接查询部门:
SQLSELECT department FROM employees;
结果可能是:
技术部 销售部 技术部 财务部 销售部
如果希望只获取不重复的部门名称,可以使用:
SQLSELECT DISTINCT department FROM employees;
查询结果:
技术部 销售部 财务部
这里的 DISTINCT 并不是修改数据库中的原始数据,而只是对当前查询结果进行去重。
二、DISTINCT可以对多个字段同时去重
这是使用 SELECT DISTINCT 时非常重要的一点。
当查询多个字段时,DISTINCT 判断的是整行结果是否重复,而不是分别对每个字段进行去重。
例如:
SQLSELECT DISTINCT department, job FROM employees;
假设原始数据如下:
department job 技术部 程序员 技术部 测试工程师 技术部 程序员 销售部 销售经理 销售部 销售人员
执行查询后:
技术部 程序员 技术部 测试工程师 销售部 销售经理 销售部 销售人员
其中两个“技术部 + 程序员”完全相同,因此只保留一条。
需要注意的是:
SQLSELECT DISTINCT department, job
并不是分别取得“唯一部门”和“唯一职位”,而是取得唯一的“部门 + 职位”组合。
三、SELECT DISTINCT与普通SELECT的区别
普通 SELECT 不会主动消除重复结果:
SQLSELECT department FROM employees;
如果表中存在多个相同部门,就会返回多条记录。
加入 DISTINCT 后:
SQLSELECT DISTINCT department FROM employees;
重复的部门只返回一次。
因此,可以简单理解为:
SELECT → 返回符合条件的查询结果 SELECT DISTINCT → 返回去除重复后的查询结果
需要强调的是,DISTINCT 只影响查询结果,不会删除表中的重复数据。
例如:
SQLSELECT DISTINCT department FROM employees;
执行后,数据库中的重复部门数据仍然存在。
如果真正需要删除重复记录,需要根据业务规则使用 DELETE、唯一索引或其他数据治理方案,而不能把 DISTINCT 当成数据删除工具。
四、DISTINCT与WHERE结合使用
DISTINCT 可以与 WHERE 一起使用,先筛选数据,再对查询结果去重。
例如,只查询技术部门和销售部门:
SQLSELECT DISTINCT department FROM employees WHERE department IN ('技术部', '销售部');
查询结果:
技术部 销售部
其逻辑可以理解为:
-
从员工表中找到符合
WHERE条件的数据; -
从筛选后的结果中选择
department; -
删除重复的部门值;
-
返回最终结果。
因此,以下语句:
SQLSELECT DISTINCT department FROM employees WHERE status = '在职';
表示查询所有在职员工所属的不同部门。
五、DISTINCT与ORDER BY结合使用
如果希望去重后按照一定顺序显示,可以结合 ORDER BY:
SQLSELECT DISTINCT department FROM employees ORDER BY department;
这样得到的部门列表会按照 department 的排序规则排列。
也可以进行降序排序:
SQLSELECT DISTINCT department FROM employees ORDER BY department DESC;
如果查询多个字段:
SQLSELECT DISTINCT department, job FROM employees ORDER BY department, job;
可以先去除重复组合,再按照部门和职位排序。
不过,不同数据库系统对于 DISTINCT 与 ORDER BY 的组合存在一些细节差异。尤其是 ORDER BY 中使用未出现在 SELECT 列表里的字段时,应根据具体数据库的语法规则进行确认。
六、DISTINCT如何处理NULL
NULL 是 SQL 查询中容易产生疑惑的特殊值。
如果某个字段存在多个 NULL:
department 技术部 NULL 销售部 NULL 技术部
执行:
SQLSELECT DISTINCT department FROM employees;
通常会得到:
技术部 NULL 销售部
也就是说,多个 NULL 在 DISTINCT 的结果中通常只保留一个 NULL。
这里要注意,SQL 中的 NULL 并不等同于普通意义上的空字符串 ''。
例如:
NULL '' ' '
这三个值在数据库语义上并不相同,使用 DISTINCT 时也不能简单认为它们是同一个值。
如果业务上不需要 NULL,可以显式过滤:
SQLSELECT DISTINCT department FROM employees WHERE department IS NOT NULL;
七、SELECT DISTINCT统计唯一值数量
DISTINCT 经常与聚合函数 COUNT() 配合使用,用于统计某个字段中不同值的数量。
例如:
SQLSELECT COUNT(DISTINCT department) FROM employees;
如果员工表中共有技术部、销售部和财务部三个不同部门,那么结果就是:
3
这种写法非常适合统计:
-
不同用户数量
-
不同商品数量
-
不同部门数量
-
不同城市数量
-
不同订单客户数量
-
不同设备数量
例如统计不同客户:
SQLSELECT COUNT(DISTINCT customer_id) FROM orders;
它表示统计订单表中出现过的不同客户数量,而不是订单总数。
COUNT(*)与COUNT(DISTINCT)的区别
假设订单表有100条记录,但实际只有35个不同客户。
SQLSELECT COUNT(*) FROM orders;
结果为:
100
而:
SQLSELECT COUNT(DISTINCT customer_id) FROM orders;
结果为:
35
前者统计记录数量,后者统计指定字段的唯一值数量。
八、DISTINCT与GROUP BY有什么区别
DISTINCT 和 GROUP BY 在某些简单查询中可以得到类似结果。
例如:
SQLSELECT DISTINCT department FROM employees;
与:
SQLSELECT department FROM employees GROUP BY department;
都可以得到唯一部门列表。
但两者的设计目的并不完全相同。
DISTINCT 更适合表达:
我只需要不重复的查询结果。
而 GROUP BY 更适合表达:
我要按照某个字段分组,并进一步进行统计。
例如统计每个部门的员工人数:
SQLSELECT department, COUNT(*) AS employee_count FROM employees GROUP BY department;
查询结果可能是:
技术部 20 销售部 15 财务部 8
如果只是需要部门名称,没有统计需求:
SQLSELECT DISTINCT department FROM employees;
通常更加直接。
因此,不能简单认为 DISTINCT 和 GROUP BY 是完全可以互换的语法。
九、DISTINCT与聚合函数结合
除了 COUNT(DISTINCT column),还可以将 DISTINCT 与其他查询逻辑结合。
例如获取不同城市:
SQLSELECT DISTINCT city FROM customers;
如果希望统计每个城市的客户数量,则应该使用:
SQLSELECT city, COUNT(*) AS customer_count FROM customers GROUP BY city;
这两个查询虽然都涉及“城市”,但实际需求不同。
前者关注:
有哪些不同城市?
后者关注:
每个城市有多少客户?
明确查询目标后再选择 DISTINCT 或 GROUP BY,SQL 会更加清晰。
十、DISTINCT与LIMIT结合
在支持 LIMIT 的数据库中,可以先去重,再限制返回数量。
例如:
SQLSELECT DISTINCT city FROM customers LIMIT 10;
表示从查询结果中获取不重复的城市,并限制返回10条。
如果需要排序后再获取前10个唯一城市,可以写成:
SQLSELECT DISTINCT city FROM customers ORDER BY city LIMIT 10;
需要注意,LIMIT、TOP、FETCH FIRST 等限制结果集的语法会因数据库产品不同而有所差异。
例如 SQL Server 常见写法是:
SQLSELECT DISTINCT TOP 10 city FROM customers ORDER BY city;
因此,在实际项目中使用时,应结合 MySQL、SQL Server、PostgreSQL、Oracle 等具体数据库的语法规范。
十一、DISTINCT与JOIN结合使用
DISTINCT 在多表查询中也非常常见。
例如客户表:
customers id name
订单表:
orders id customer_id product_id
一个客户可能拥有多个订单。如果直接关联查询:
SQLSELECT c.id, c.name FROM customers c JOIN orders o ON c.id = o.customer_id;
同一个客户可能因为存在多条订单而出现多次。
如果业务需求只是获取“有订单的客户列表”,可以使用:
SQLSELECT DISTINCT c.id, c.name FROM customers c JOIN orders o ON c.id = o.customer_id;
这样同一客户即使存在多个订单,也只会在结果中出现一次。
不过,如果只是判断某个客户是否存在相关记录,也可以考虑 EXISTS:
SQLSELECT c.id, c.name FROM customers c WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.id );
在复杂查询中,EXISTS 有时比“先 JOIN 再 DISTINCT”更能直接表达业务意图,具体性能则需要结合执行计划和数据规模判断。
十二、DISTINCT可能带来的性能问题
DISTINCT 使用起来非常方便,但并不意味着可以在所有查询中随意添加。
数据库为了确定哪些结果重复,通常需要进行排序、哈希或其他去重处理。当查询数据量非常大时,这一步可能产生额外的 CPU、内存和磁盘开销。
例如:
SQLSELECT DISTINCT * FROM large_table;
如果 large_table 有数百万甚至更多数据,并且包含大量字段,那么数据库需要对大量数据进行重复判断。
因此,不建议为了“保险”而在所有 SQL 查询中使用 DISTINCT。
更合理的做法是明确查询需求:
SQLSELECT DISTINCT department FROM employees;
比:
SQLSELECT DISTINCT * FROM employees;
更容易控制查询的数据量和去重范围。
十三、不要用DISTINCT掩盖JOIN逻辑错误
实际开发中,一个比较常见的问题是:
SQLSELECT DISTINCT ... FROM A JOIN B ... JOIN C ...
查询出现大量重复数据后,直接添加 DISTINCT。
虽然最终结果可能看起来正确,但这并不一定解决了根本问题。
例如,一对多关系 JOIN 时,如果本来就应该返回多条明细记录,却使用 DISTINCT 强制去重,可能导致合法数据被合并。
因此,遇到重复结果时,建议先检查:
-
JOIN 条件是否正确;
-
表之间的关系是否为一对一、一对多或多对多;
-
是否遗漏了必要的过滤条件;
-
查询字段是否真的需要去重;
-
是否应该使用
GROUP BY; -
是否应该使用
EXISTS。
DISTINCT 应该用于明确的去重需求,而不是用来掩盖查询逻辑问题。
十四、DISTINCT常见错误用法
1. 误以为DISTINCT只作用于第一个字段
错误理解:
SQLSELECT DISTINCT department, job FROM employees;
认为它只会对 department 去重。
实际上,数据库判断的是:
department + job
整个结果组合是否重复。
2. 把DISTINCT当成删除重复数据
下面语句:
SQLSELECT DISTINCT name FROM users;
只是返回唯一姓名,并不会删除 users 表中的重复记录。
3. 盲目使用SELECT DISTINCT *
例如:
SQLSELECT DISTINCT * FROM orders;
如果表中存在主键、创建时间等本身就不同的字段,即使业务上两条数据看起来相同,整行数据仍然可能不同,因此 DISTINCT 也无法实现预期的业务去重。
4. 忽略查询字段的影响
下面两个 SQL:
SQLSELECT DISTINCT department FROM employees;
和:
SQLSELECT DISTINCT department, job FROM employees;
结果数量可能完全不同。
增加一个字段,就可能增加新的唯一组合。
十五、实际项目中的使用建议
使用 SELECT DISTINCT 时,可以遵循几个原则。
首先,明确自己需要去重的是哪个结果维度。如果只需要唯一部门,就查询部门字段,而不是直接查询整行。
其次,多个字段同时使用 DISTINCT 时,要按照“组合唯一”的思路理解结果。
再次,如果查询目的是统计分组数据,优先考虑 GROUP BY,不要单纯依赖 DISTINCT。
对于复杂 JOIN,如果重复结果来自表关系本身,应优先检查 JOIN 条件,而不是直接添加 DISTINCT。
最后,对于大数据量查询,需要通过执行计划观察排序、哈希、临时表等操作产生的成本,并结合索引和查询结构进行优化。
十六、SELECT DISTINCT常用示例汇总
查询唯一部门:
SQLSELECT DISTINCT department FROM employees;
查询唯一部门和职位组合:
SQLSELECT DISTINCT department, job FROM employees;
查询在职员工涉及的不同部门:
SQLSELECT DISTINCT department FROM employees WHERE status = '在职';
查询唯一部门并排序:
SQLSELECT DISTINCT department FROM employees ORDER BY department;
统计不同部门数量:
SQLSELECT COUNT(DISTINCT department) FROM employees;
统计不同客户数量:
SQLSELECT COUNT(DISTINCT customer_id) FROM orders;
查询有订单的不同客户:
SQLSELECT DISTINCT customer_id FROM orders;
过滤 NULL 后查询唯一值:
SQLSELECT DISTINCT department FROM employees WHERE department IS NOT NULL;
十七、SELECT DISTINCT使用要点总结
SELECT DISTINCT 的核心作用是消除查询结果中的重复行。它不会修改原始表数据,也不能直接代替数据清洗或重复数据删除操作。
使用时尤其需要掌握以下几点:
-
DISTINCT通常写在SELECT关键字之后; -
多字段查询时,按照整行字段组合判断重复;
-
多个
NULL通常只保留一个结果; -
可以与
WHERE、ORDER BY、JOIN、COUNT()等语句或函数结合使用; -
COUNT(DISTINCT column)用于统计唯一值数量; -
简单去重适合使用
DISTINCT,分组统计通常使用GROUP BY; -
JOIN 后出现重复记录时,应先检查关联逻辑;
-
大数据量查询中应关注
DISTINCT带来的排序、哈希和临时数据处理成本; -
不建议无明确需求地使用
SELECT DISTINCT *。
掌握这些规则后,SELECT DISTINCT 不仅可以用于简单的数据去重,还能应用于客户分析、订单查询、用户统计、分类筛选以及多表关联等实际数据库场景。