SQL SELECT DISTINCT语句的用法详解

10 次阅读

SELECT DISTINCT 是 SQL 中用于去除查询结果重复行的常用语法,适合处理数据去重、分类统计前的数据整理以及唯一值查询等场景。理解 DISTINCT 的作用范围、NULL 处理方式以及与 GROUP BY、COUNT 的区别,可以避免查询结果出现重复数据或统计结果不符合预期的问题。

一、SELECT DISTINCT的基本语法

DISTINCT 通常紧跟在 SELECT 后面,用于要求查询结果中的记录保持唯一。

基本语法如下:

SQL
SELECT DISTINCT column_name
FROM table_name;

例如,有一张员工表 employees:

id   name    department
1    张三    技术部
2    李四    销售部
3    王五    技术部
4    赵六    财务部
5    钱七    销售部

如果直接查询部门:

SQL
SELECT department
FROM employees;

结果可能是:

技术部
销售部
技术部
财务部
销售部

如果希望只获取不重复的部门名称,可以使用:

SQL
SELECT DISTINCT department
FROM employees;

查询结果:

技术部
销售部
财务部

这里的 DISTINCT 并不是修改数据库中的原始数据,而只是对当前查询结果进行去重。

二、DISTINCT可以对多个字段同时去重

这是使用 SELECT DISTINCT 时非常重要的一点。

当查询多个字段时,DISTINCT 判断的是整行结果是否重复,而不是分别对每个字段进行去重。

例如:

SQL
SELECT DISTINCT department, job
FROM employees;

假设原始数据如下:

department   job
技术部       程序员
技术部       测试工程师
技术部       程序员
销售部       销售经理
销售部       销售人员

执行查询后:

技术部       程序员
技术部       测试工程师
销售部       销售经理
销售部       销售人员

其中两个“技术部 + 程序员”完全相同,因此只保留一条。

需要注意的是:

SQL
SELECT DISTINCT department, job

并不是分别取得“唯一部门”和“唯一职位”,而是取得唯一的“部门 + 职位”组合。

三、SELECT DISTINCT与普通SELECT的区别

普通 SELECT 不会主动消除重复结果:

SQL
SELECT department
FROM employees;

如果表中存在多个相同部门,就会返回多条记录。

加入 DISTINCT 后:

SQL
SELECT DISTINCT department
FROM employees;

重复的部门只返回一次。

因此,可以简单理解为:

SELECT       → 返回符合条件的查询结果
SELECT DISTINCT → 返回去除重复后的查询结果

需要强调的是,DISTINCT 只影响查询结果,不会删除表中的重复数据。

例如:

SQL
SELECT DISTINCT department
FROM employees;

执行后,数据库中的重复部门数据仍然存在。

如果真正需要删除重复记录,需要根据业务规则使用 DELETE、唯一索引或其他数据治理方案,而不能把 DISTINCT 当成数据删除工具。

四、DISTINCT与WHERE结合使用

DISTINCT 可以与 WHERE 一起使用,先筛选数据,再对查询结果去重。

例如,只查询技术部门和销售部门:

SQL
SELECT DISTINCT department
FROM employees
WHERE department IN ('技术部', '销售部');

查询结果:

技术部
销售部

其逻辑可以理解为:

  1. 从员工表中找到符合 WHERE 条件的数据;

  2. 从筛选后的结果中选择 department;

  3. 删除重复的部门值;

  4. 返回最终结果。

因此,以下语句:

SQL
SELECT DISTINCT department
FROM employees
WHERE status = '在职';

表示查询所有在职员工所属的不同部门。

五、DISTINCT与ORDER BY结合使用

如果希望去重后按照一定顺序显示,可以结合 ORDER BY:

SQL
SELECT DISTINCT department
FROM employees
ORDER BY department;

这样得到的部门列表会按照 department 的排序规则排列。

也可以进行降序排序:

SQL
SELECT DISTINCT department
FROM employees
ORDER BY department DESC;

如果查询多个字段:

SQL
SELECT DISTINCT department, job
FROM employees
ORDER BY department, job;

可以先去除重复组合,再按照部门和职位排序。

不过,不同数据库系统对于 DISTINCT 与 ORDER BY 的组合存在一些细节差异。尤其是 ORDER BY 中使用未出现在 SELECT 列表里的字段时,应根据具体数据库的语法规则进行确认。

六、DISTINCT如何处理NULL

NULL 是 SQL 查询中容易产生疑惑的特殊值。

如果某个字段存在多个 NULL:

department
技术部
NULL
销售部
NULL
技术部

执行:

SQL
SELECT DISTINCT department
FROM employees;

通常会得到:

技术部
NULL
销售部

也就是说,多个 NULL 在 DISTINCT 的结果中通常只保留一个 NULL。

这里要注意,SQL 中的 NULL 并不等同于普通意义上的空字符串 ''。

例如:

NULL
''
' '

这三个值在数据库语义上并不相同,使用 DISTINCT 时也不能简单认为它们是同一个值。

如果业务上不需要 NULL,可以显式过滤:

SQL
SELECT DISTINCT department
FROM employees
WHERE department IS NOT NULL;

七、SELECT DISTINCT统计唯一值数量

DISTINCT 经常与聚合函数 COUNT() 配合使用,用于统计某个字段中不同值的数量。

例如:

SQL
SELECT COUNT(DISTINCT department)
FROM employees;

如果员工表中共有技术部、销售部和财务部三个不同部门,那么结果就是:

3

这种写法非常适合统计:

  • 不同用户数量

  • 不同商品数量

  • 不同部门数量

  • 不同城市数量

  • 不同订单客户数量

  • 不同设备数量

例如统计不同客户:

SQL
SELECT COUNT(DISTINCT customer_id)
FROM orders;

它表示统计订单表中出现过的不同客户数量,而不是订单总数。

COUNT(*)与COUNT(DISTINCT)的区别

假设订单表有100条记录,但实际只有35个不同客户。

SQL
SELECT COUNT(*)
FROM orders;

结果为:

100

而:

SQL
SELECT COUNT(DISTINCT customer_id)
FROM orders;

结果为:

35

前者统计记录数量,后者统计指定字段的唯一值数量。

八、DISTINCT与GROUP BY有什么区别

DISTINCT 和 GROUP BY 在某些简单查询中可以得到类似结果。

例如:

SQL
SELECT DISTINCT department
FROM employees;

与:

SQL
SELECT department
FROM employees
GROUP BY department;

都可以得到唯一部门列表。

但两者的设计目的并不完全相同。

DISTINCT 更适合表达:

我只需要不重复的查询结果。

而 GROUP BY 更适合表达:

我要按照某个字段分组,并进一步进行统计。

例如统计每个部门的员工人数:

SQL
SELECT department, COUNT(*) AS employee_count
FROM employees
GROUP BY department;

查询结果可能是:

技术部   20
销售部   15
财务部   8

如果只是需要部门名称,没有统计需求:

SQL
SELECT DISTINCT department
FROM employees;

通常更加直接。

因此,不能简单认为 DISTINCT 和 GROUP BY 是完全可以互换的语法。

九、DISTINCT与聚合函数结合

除了 COUNT(DISTINCT column),还可以将 DISTINCT 与其他查询逻辑结合。

例如获取不同城市:

SQL
SELECT DISTINCT city
FROM customers;

如果希望统计每个城市的客户数量,则应该使用:

SQL
SELECT city, COUNT(*) AS customer_count
FROM customers
GROUP BY city;

这两个查询虽然都涉及“城市”,但实际需求不同。

前者关注:

有哪些不同城市?

后者关注:

每个城市有多少客户?

明确查询目标后再选择 DISTINCT 或 GROUP BY,SQL 会更加清晰。

十、DISTINCT与LIMIT结合

在支持 LIMIT 的数据库中,可以先去重,再限制返回数量。

例如:

SQL
SELECT DISTINCT city
FROM customers
LIMIT 10;

表示从查询结果中获取不重复的城市,并限制返回10条。

如果需要排序后再获取前10个唯一城市,可以写成:

SQL
SELECT DISTINCT city
FROM customers
ORDER BY city
LIMIT 10;

需要注意,LIMIT、TOP、FETCH FIRST 等限制结果集的语法会因数据库产品不同而有所差异。

例如 SQL Server 常见写法是:

SQL
SELECT 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

一个客户可能拥有多个订单。如果直接关联查询:

SQL
SELECT c.id, c.name
FROM customers c
JOIN orders o ON c.id = o.customer_id;

同一个客户可能因为存在多条订单而出现多次。

如果业务需求只是获取“有订单的客户列表”,可以使用:

SQL
SELECT DISTINCT c.id, c.name
FROM customers c
JOIN orders o ON c.id = o.customer_id;

这样同一客户即使存在多个订单,也只会在结果中出现一次。

不过,如果只是判断某个客户是否存在相关记录,也可以考虑 EXISTS:

SQL
SELECT 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、内存和磁盘开销。

例如:

SQL
SELECT DISTINCT *
FROM large_table;

如果 large_table 有数百万甚至更多数据,并且包含大量字段,那么数据库需要对大量数据进行重复判断。

因此,不建议为了“保险”而在所有 SQL 查询中使用 DISTINCT。

更合理的做法是明确查询需求:

SQL
SELECT DISTINCT department
FROM employees;

比:

SQL
SELECT DISTINCT *
FROM employees;

更容易控制查询的数据量和去重范围。

十三、不要用DISTINCT掩盖JOIN逻辑错误

实际开发中,一个比较常见的问题是:

SQL
SELECT DISTINCT ...
FROM A
JOIN B ...
JOIN C ...

查询出现大量重复数据后,直接添加 DISTINCT。

虽然最终结果可能看起来正确,但这并不一定解决了根本问题。

例如,一对多关系 JOIN 时,如果本来就应该返回多条明细记录,却使用 DISTINCT 强制去重,可能导致合法数据被合并。

因此,遇到重复结果时,建议先检查:

  • JOIN 条件是否正确;

  • 表之间的关系是否为一对一、一对多或多对多;

  • 是否遗漏了必要的过滤条件;

  • 查询字段是否真的需要去重;

  • 是否应该使用 GROUP BY;

  • 是否应该使用 EXISTS。

DISTINCT 应该用于明确的去重需求,而不是用来掩盖查询逻辑问题。

十四、DISTINCT常见错误用法

1. 误以为DISTINCT只作用于第一个字段

错误理解:

SQL
SELECT DISTINCT department, job
FROM employees;

认为它只会对 department 去重。

实际上,数据库判断的是:

department + job

整个结果组合是否重复。

2. 把DISTINCT当成删除重复数据

下面语句:

SQL
SELECT DISTINCT name
FROM users;

只是返回唯一姓名,并不会删除 users 表中的重复记录。

3. 盲目使用SELECT DISTINCT *

例如:

SQL
SELECT DISTINCT *
FROM orders;

如果表中存在主键、创建时间等本身就不同的字段,即使业务上两条数据看起来相同,整行数据仍然可能不同,因此 DISTINCT 也无法实现预期的业务去重。

4. 忽略查询字段的影响

下面两个 SQL:

SQL
SELECT DISTINCT department
FROM employees;

和:

SQL
SELECT DISTINCT department, job
FROM employees;

结果数量可能完全不同。

增加一个字段,就可能增加新的唯一组合。

十五、实际项目中的使用建议

使用 SELECT DISTINCT 时,可以遵循几个原则。

首先,明确自己需要去重的是哪个结果维度。如果只需要唯一部门,就查询部门字段,而不是直接查询整行。

其次,多个字段同时使用 DISTINCT 时,要按照“组合唯一”的思路理解结果。

再次,如果查询目的是统计分组数据,优先考虑 GROUP BY,不要单纯依赖 DISTINCT。

对于复杂 JOIN,如果重复结果来自表关系本身,应优先检查 JOIN 条件,而不是直接添加 DISTINCT。

最后,对于大数据量查询,需要通过执行计划观察排序、哈希、临时表等操作产生的成本,并结合索引和查询结构进行优化。

十六、SELECT DISTINCT常用示例汇总

查询唯一部门:

SQL
SELECT DISTINCT department
FROM employees;

查询唯一部门和职位组合:

SQL
SELECT DISTINCT department, job
FROM employees;

查询在职员工涉及的不同部门:

SQL
SELECT DISTINCT department
FROM employees
WHERE status = '在职';

查询唯一部门并排序:

SQL
SELECT DISTINCT department
FROM employees
ORDER BY department;

统计不同部门数量:

SQL
SELECT COUNT(DISTINCT department)
FROM employees;

统计不同客户数量:

SQL
SELECT COUNT(DISTINCT customer_id)
FROM orders;

查询有订单的不同客户:

SQL
SELECT DISTINCT customer_id
FROM orders;

过滤 NULL 后查询唯一值:

SQL
SELECT 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 不仅可以用于简单的数据去重,还能应用于客户分析、订单查询、用户统计、分类筛选以及多表关联等实际数据库场景。