SQL中的DISTINCT关键字用于对查询结果集进行去重,它的核心作用是返回结果中不存在完全相同的行,其去重逻辑基于行的所有选中列的组合值是否完全一致来判断。

DISTINCT的基本去重规则
DISTINCT的去重判断是基于SELECT后面跟随的所有列的值组合来进行的,只有当所有选中列的值都完全相同时,才会被判定为重复行,最终只保留其中一行。
单列去重场景
当只对单列使用DISTINCT时,会去掉该列值重复的所有行,只保留每个不同值对应的一行。比如有一张用户表user_info,结构如下:
| id | user_name | city |
|---|---|---|
| 1 | 张三 | 北京 |
| 2 | 李四 | 上海 |
| 3 | 王五 | 北京 |
| 4 | 赵六 | 广州 |
| 5 | 张三 | 深圳 |
如果要查询所有不重复的城市,使用如下SQL:
-- 查询不重复的城市 SELECT DISTINCT city FROM user_info;
执行结果会返回北京、上海、广州、深圳四个城市,原本表中北京出现了两次,去重后只保留一次。
多列去重场景
当SELECT后面跟随多列时,DISTINCT会判断这些列的组合值是否重复,只有所有列的值都完全相同时才会去重。比如查询不重复的用户名和城市组合:
-- 查询不重复的用户名和城市组合 SELECT DISTINCT user_name, city FROM user_info;
执行结果会返回5行数据,因为虽然张三出现了两次,但对应的城市分别是北京和深圳,组合值不同,所以不会被判定为重复行。
DISTINCT的执行过程
DISTINCT的去重过程通常发生在SQL查询的执行阶段,大致可以分为两个步骤:
- 第一步:先执行FROM、WHERE等子句,得到初步的结果集,这个结果集包含所有符合筛选条件的行。
- 第二步:对初步结果集进行去重处理,数据库会对SELECT后面的所有列的值进行哈希计算或者排序,将相同的行合并,最终只保留唯一的行返回给客户端。
不同的数据库引擎实现DISTINCT的具体方式可能有差异,比如MySQL在处理DISTINCT时,如果无法使用索引,可能会先对结果集进行排序,然后遍历排序后的结果,去掉相邻重复的行;如果可以使用索引,会直接利用索引的有序性快速去重。
DISTINCT使用的注意事项
在使用DISTINCT时需要注意几个常见的问题,避免出现不符合预期的查询结果:
NULL值的处理
在SQL中,NULL代表未知值,DISTINCT会把所有的NULL值判定为相等,所以如果某列存在多个NULL值,去重后只会保留一个NULL行。比如表中city列有多个NULL值,查询DISTINCT city时只会返回一个NULL。
与ORDER BY的配合
DISTINCT之后的结果集如果需要排序,ORDER BY后面的列必须是SELECT中已经出现的列,或者是这些列的表达式,否则会报错。比如下面的SQL是不合法的:
-- 错误示例:ORDER BY的列不在SELECT中 SELECT DISTINCT city FROM user_info ORDER BY user_name;
正确的写法应该是ORDER BY后面的列是已经选中的city,或者把user_name也加入SELECT列表:
-- 正确示例1:ORDER BY选中的列 SELECT DISTINCT city FROM user_info ORDER BY city; -- 正确示例2:把user_name加入SELECT列表 SELECT DISTINCT user_name, city FROM user_info ORDER BY user_name;
性能影响
DISTINCT操作需要对结果集进行额外的去重处理,当结果集数据量较大时,会带来一定的性能开销。如果频繁需要对某列去重,可以考虑给该列建立索引,提升DISTINCT的执行效率。另外,不要滥用DISTINCT,只有在确实需要去重的时候才使用,避免不必要的性能消耗。
简单示例验证
我们可以通过一个完整的示例来验证DISTINCT的去重逻辑,首先创建测试表并插入数据:
-- 创建测试表
CREATE TABLE test_distinct (
id INT,
col1 VARCHAR(20),
col2 VARCHAR(20)
);
-- 插入测试数据
INSERT INTO test_distinct VALUES (1, 'a', 'x');
INSERT INTO test_distinct VALUES (2, 'a', 'x');
INSERT INTO test_distinct VALUES (3, 'a', 'y');
INSERT INTO test_distinct VALUES (4, 'b', 'x');
INSERT INTO test_distinct VALUES (5, NULL, 'x');
INSERT INTO test_distinct VALUES (6, NULL, 'y');
然后执行不同的DISTINCT查询:
-- 单列col1去重 SELECT DISTINCT col1 FROM test_distinct; -- 结果:a、b、NULL,共3行 -- 多列col1和col2去重 SELECT DISTINCT col1, col2 FROM test_distinct; -- 结果:(a,x)、(a,y)、(b,x)、(NULL,x)、(NULL,y),共5行
从结果可以清晰看到DISTINCT的去重是基于选中列的组合值来判断的,NULL值也会被合并为一个。