乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 数据库基础与安装

    • MySQL 简介
    • MySQL 5.6 到 5.7 到 8.0 关键差异速查
    • 安装 MySQL 5.7
    • 连接与断开服务器
    • 创建数据库
    • 创建数据表
    • 数据库与数据表
    • 加载数据
    • 获取数据库信息
    • 批处理模式
    • SHOW 语句汇总
    • FLUSH 与 RESET 语句
    • my.cnf 核心参数
    • 字符集与排序规则
  • 第2章 SQL基础查询

    • SELECT
    • WHERE
    • ORDER BY
    • LIMIT
    • COUNT
    • 聚合函数
    • 比较运算符
    • 逻辑运算符
    • 算术运算符
    • 模式匹配
    • NULL 值处理
    • UPDATE
    • DELETE
    • REPLACE
    • SELECT INTO
  • 第3章 数据类型与运算符

    • 数值类型
    • 字符串类型
    • 日期时间类型
    • BIT 类型
    • ENUM 类型
    • SET 类型
    • JSON 类型
    • 类型转换
  • 第4章 函数与表达式

    • 字符串函数
    • 数值函数
    • 日期函数
    • 全文检索函数
  • 第5章 高级查询与子查询

    • JOIN
    • 子查询
    • UNION
    • GROUP BY
    • HAVING
    • DISTINCT
  • 第6章 表与索引

    • 数据定义语言
    • 修改表结构
    • 视图
    • 修改视图与检查选项
    • 外键
    • 索引
    • 唯一索引
    • 复合索引
    • 存储引擎对比
    • 分区表
    • 第一范式与第二范式
    • 第三范式与 BC 范式
    • 反范式设计
  • 第7章 存储过程与函数

    • 存储过程
    • 存储函数
    • 变量
    • 流程控制
    • 游标
    • 预处理语句
  • 第8章 事务与锁

    • 事务
    • 事务隔离级别
    • 锁机制
    • MVCC
    • 死锁专题分析
    • LOCK TABLES
    • XA 事务
  • 第9章 用户管理与安全

    • 用户管理
    • 权限管理
    • 角色
    • SQL 注入防范
  • 第10章 性能优化入门

    • 执行计划
    • 索引优化
    • 查询优化
    • 查询优化器提示
    • 慢查询日志
    • InnoDB 深入机制
    • InnoDB 专项优化
    • Performance Schema
    • sys Schema
  • 第11章 复制与高可用

    • 主从复制原理
    • 半同步复制配置
    • binlog 开启与 point-in-time 恢复
    • mysqldump 全库备份
    • mysqldump 单表与条件备份
    • mysqldump 恢复与导入
    • xtrabackup 全量热备
    • xtrabackup 准备与恢复
    • xtrabackup 增量与流式备份
  • 第12章 触发器与事件

    • 触发器
    • 事件调度器
  • 参考

    • MySQL 5.7 专业术语大全
    • MySQL 5.7 关键字与保留字大全

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_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

执行单列去重

SELECT DISTINCT dept FROM employees;

操作后的结果

dept
技术部

结果解读

  • DISTINCT dept 对 dept 列去重,只保留唯一的值
  • 原表有 2 行,去重后只剩 1 个不同的部门

完整示例二:多列组合去重

当前数据状态

基于上面的 employees 表。

执行多列去重

SELECT DISTINCT dept, emp_name FROM employees;

操作后的结果

deptemp_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_namedept
大翔技术部
白歌技术部

结果解读

  • 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_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

结果解读

  • DISTINCT 只能用于 SELECT 列表,不能用于 WHERE
  • WHERE 中如需去重逻辑,应考虑使用 EXISTS 或 IN 配合子查询

DISTINCT vs GROUP BY

特性DISTINCTGROUP 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 中)

下一章引子:查询语句掌握得差不多了,接下来进入数据库的"结构工程"——如何定义和修改表结构,以及如何设计高效的索引。

上一页
HAVING