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

    • 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 关键字与保留字大全

比较运算符

导学

WHERE 子句的灵魂是条件判断,而条件判断的核心是比较运算符。本节系统梳理 MySQL 5.7 中的所有比较运算符,并重点辨析容易混淆的用法。

定义

比较运算符:用于比较两个表达式的值,返回 1(真)、0(假)或 NULL(未知)。比较结果是 WHERE 和 HAVING 子句进行行/组过滤的依据。

运算符一览

运算符含义示例
=等于WHERE dept = '技术部'
<> 或 !=不等于WHERE dept <> '运营部'
>大于WHERE score > 60
<小于WHERE score < 100
>=大于等于WHERE score >= 88
<=小于等于WHERE score <= 100
<=>安全等于(NULL 安全)WHERE col <=> NULL
BETWEEN ... AND ...在范围内(闭区间)WHERE score BETWEEN 88 AND 100
IN (...)在集合中WHERE dept IN ('技术部', '产品部')
NOT IN (...)不在集合中WHERE dept NOT IN ('技术部')
IS NULL是 NULLWHERE score IS NULL
IS NOT NULL不是 NULLWHERE score IS NOT NULL
LIKE模式匹配WHERE emp_name LIKE '张%'
REGEXP 或 RLIKE正则匹配WHERE emp_name REGEXP '^大'

演示数据准备

DROP TABLE IF EXISTS employees;

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);

当前 employees 表中的完整数据如下:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

SQL 示例

场景一:等于与不等于

当前数据状态:见上文 employees 表完整数据。

执行语句(等于):

SELECT emp_name, dept FROM employees WHERE dept = '技术部';

操作后结果:

emp_namedept
大翔技术部
白歌技术部

结果解读:= 精确匹配字符串'技术部',返回 2 行。MySQL 字符串比较默认不区分大小写。

执行语句(不等于):

SELECT emp_name, dept FROM employees WHERE dept <> '技术部';
-- 等价于
-- SELECT emp_name, dept FROM employees WHERE dept != '技术部';

操作后结果:

emp_namedept

结果解读:<> 和 != 都表示不等于,排除了技术部的员工。当前表中所有员工都在技术部,因此返回空结果。

场景二:大于、小于、大于等于、小于等于

当前数据状态:见上文 employees 表完整数据。

执行语句(大于):

SELECT emp_name, score FROM employees WHERE score > 90;

操作后结果:

emp_namescore
大翔100

结果解读:返回 score 严格大于 90 的员工。白歌(score=NULL)因 NULL 比较结果为 UNKNOWN 也被排除。

执行语句(大于等于):

SELECT emp_name, score FROM employees WHERE score >= 88;

操作后结果:

emp_namescore
大翔100

结果解读:>= 包含边界值。当前表中只有大翔的 score 为 100,白歌为 NULL 被排除,因此返回 1 行。

执行语句(小于):

SELECT emp_name, score FROM employees WHERE score < 90;

操作后结果:

emp_namescore

结果解读:返回 score 严格小于 90 的员工。注意白歌的 score 为 NULL,NULL 与任何值比较结果都是 UNKNOWN,因此被排除。

场景三:范围查询(BETWEEN)

当前数据状态:见上文 employees 表完整数据。

执行语句:

SELECT emp_name, score FROM employees
WHERE score BETWEEN 88 AND 100;

操作后结果:

emp_namescore
大翔100

结果解读:BETWEEN 是闭区间,包含两个边界值 88 和 100。它等价于 score >= 88 AND score <= 100。白歌(NULL)被排除。

注意:BETWEEN 是闭区间,始终包含边界值。对于整数和浮点数,边界值本身一定会被包含在结果中。

场景四:NULL 安全的比较(<=>)

当前数据状态:见上文 employees 表完整数据。

执行语句(普通等于与 NULL 比较):

SELECT NULL = NULL;    -- 结果:NULL
SELECT 1 = NULL;       -- 结果:NULL
SELECT emp_name, score FROM employees WHERE score = NULL;

操作后结果:SELECT emp_name, score FROM employees WHERE score = NULL; 返回空结果集。

结果解读:普通 = 与 NULL 比较结果永远是 NULL(UNKNOWN),WHERE 只保留 TRUE,因此没有任何行被返回。

执行语句(安全等于):

SELECT NULL <=> NULL;  -- 结果:1(TRUE)
SELECT 1 <=> NULL;     -- 结果:0(FALSE)
SELECT emp_name, score FROM employees WHERE score <=> NULL;

操作后结果:

emp_namescore
白歌NULL

结果解读:<=>(太空船运算符 / NULL-safe equal)可以正确判断 NULL。NULL <=> NULL 返回 TRUE,1 <=> NULL 返回 FALSE。score <=> NULL 正确找到了白歌这一行。

但在日常查询中,判断 NULL 更推荐用 IS NULL:

SELECT emp_name, score FROM employees WHERE score IS NULL;

操作后结果:

emp_namescore
白歌NULL

场景五:IN 与 NOT IN

当前数据状态:见上文 employees 表完整数据。

执行语句(IN):

SELECT emp_name, dept FROM employees WHERE dept IN ('技术部', '产品部');

操作后结果:

emp_namedept
大翔技术部
白歌技术部

结果解读:IN 匹配列表中的任意一个值,等价于 dept = '技术部'。当前表中所有员工都在技术部。

执行语句(NOT IN):

SELECT emp_name, dept FROM employees WHERE dept NOT IN ('技术部');

操作后结果:

emp_namedept

结果解读:NOT IN 排除列表中的值,返回不在列表中的行。

场景六:NOT IN 的 NULL 陷阱

当前数据状态:见上文 employees 表完整数据。

执行语句(危险):

-- 危险:NOT IN 列表中包含 NULL,结果永远为空
SELECT emp_name FROM employees WHERE dept NOT IN ('技术部', NULL);

操作后结果:空结果集(0 行)。

结果解读:NOT IN 的本质是多次 <> 比较。dept <> NULL 的结果是 UNKNOWN,而 WHERE 只保留 TRUE 的行,因此整个查询返回空结果。这是生产环境中极易踩的坑。

正确写法:确保 IN/NOT IN 列表中不包含 NULL。

SELECT emp_name FROM employees WHERE dept NOT IN ('技术部');

操作后结果:

emp_name

场景七:字符串比较的隐藏规则

SELECT 'A' = 'a';  -- 结果:1(TRUE),默认不区分大小写
SELECT BINARY 'A' = 'a';  -- 结果:0(FALSE),强制区分大小写

结果解读:MySQL 的字符串比较是否区分大小写,取决于列的排序规则(Collation):

  • _ci(case insensitive):不区分大小写,如 utf8mb4_unicode_ci
  • _bin(binary):按二进制比较,区分大小写

我们用表数据验证:

-- 假设表中有 emp_name 字段,默认 collation 不区分大小写
SELECT 'zhangsan' = 'ZHANGSAN';  -- 结果:1
SELECT BINARY 'zhangsan' = 'ZHANGSAN';  -- 结果:0

常见误区

误区正解
WHERE column = NULL必须用 IS NULL。任何与 NULL 的比较结果都是 UNKNOWN。
NOT IN 列表里放 NULL会导致查询永远无结果,应确保 IN/NOT IN 列表中无 NULL。
BETWEEN 是开区间BETWEEN 是闭区间,包含边界值。
字符串比较默认区分大小写默认 _ci 排序规则不区分大小写。
<=> 可以替代所有 =日常查询中判断 NULL 更推荐 IS NULL,<=> 主要用于特殊场景。

面试考点

Q:<=> 和 = 的区别?

= 与 NULL 比较返回 NULL(UNKNOWN);<=> 是 NULL-safe 比较,NULL <=> NULL 返回 TRUE,1 <=> NULL 返回 FALSE。

Q:NOT IN 为什么不能包含 NULL?

NOT IN 等价于 val <> x1 AND val <> x2 AND ...。任何与 NULL 的 <> 比较结果都是 UNKNOWN,而 WHERE 只保留 TRUE,因此整个条件失效。

Q:如何比较范围最准确?

对于连续数值范围,推荐使用 >= min AND < max 模式,避免 BETWEEN 闭区间在边界值上带来的歧义。例如查询绩效分大于等于 88 且小于 100:WHERE score >= 88 AND score < 100。

Q:IN 和 = 的性能差异?

当 IN 列表只有一个元素时,MySQL 优化器通常会将 IN 转换为 =,两者性能相同。IN 列表较长时,性能取决于具体执行计划。

小结

  • 比较运算符是 WHERE 和 HAVING 的核心构件
  • NULL 参与比较时结果永远是 UNKNOWN,必须用 IS NULL
  • NOT IN 列表中不可包含 NULL,否则结果永远为空
  • 字符串比较是否区分大小写由排序规则决定
  • BETWEEN 是闭区间,范围查询推荐用 >= AND < 模式

下一章引子:单个比较条件往往不够,需要把多个条件组合起来——这就是逻辑运算符的用武之地。

上一页
聚合函数
下一页
逻辑运算符