比较运算符
导学
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 | 是 NULL | WHERE score IS NULL |
IS NOT NULL | 不是 NULL | WHERE 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_id | emp_name | dept | score |
|---|---|---|---|
| 1 | 大翔 | 技术部 | 100 |
| 2 | 白歌 | 技术部 | NULL |
SQL 示例
场景一:等于与不等于
当前数据状态:见上文 employees 表完整数据。
执行语句(等于):
SELECT emp_name, dept FROM employees WHERE dept = '技术部';
操作后结果:
| emp_name | dept |
|---|---|
| 大翔 | 技术部 |
| 白歌 | 技术部 |
结果解读:= 精确匹配字符串'技术部',返回 2 行。MySQL 字符串比较默认不区分大小写。
执行语句(不等于):
SELECT emp_name, dept FROM employees WHERE dept <> '技术部';
-- 等价于
-- SELECT emp_name, dept FROM employees WHERE dept != '技术部';
操作后结果:
| emp_name | dept |
|---|
结果解读:<> 和 != 都表示不等于,排除了技术部的员工。当前表中所有员工都在技术部,因此返回空结果。
场景二:大于、小于、大于等于、小于等于
当前数据状态:见上文 employees 表完整数据。
执行语句(大于):
SELECT emp_name, score FROM employees WHERE score > 90;
操作后结果:
| emp_name | score |
|---|---|
| 大翔 | 100 |
结果解读:返回 score 严格大于 90 的员工。白歌(score=NULL)因 NULL 比较结果为 UNKNOWN 也被排除。
执行语句(大于等于):
SELECT emp_name, score FROM employees WHERE score >= 88;
操作后结果:
| emp_name | score |
|---|---|
| 大翔 | 100 |
结果解读:>= 包含边界值。当前表中只有大翔的 score 为 100,白歌为 NULL 被排除,因此返回 1 行。
执行语句(小于):
SELECT emp_name, score FROM employees WHERE score < 90;
操作后结果:
| emp_name | score |
|---|
结果解读:返回 score 严格小于 90 的员工。注意白歌的 score 为 NULL,NULL 与任何值比较结果都是 UNKNOWN,因此被排除。
场景三:范围查询(BETWEEN)
当前数据状态:见上文 employees 表完整数据。
执行语句:
SELECT emp_name, score FROM employees
WHERE score BETWEEN 88 AND 100;
操作后结果:
| emp_name | score |
|---|---|
| 大翔 | 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_name | score |
|---|---|
| 白歌 | 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_name | score |
|---|---|
| 白歌 | NULL |
场景五:IN 与 NOT IN
当前数据状态:见上文 employees 表完整数据。
执行语句(IN):
SELECT emp_name, dept FROM employees WHERE dept IN ('技术部', '产品部');
操作后结果:
| emp_name | dept |
|---|---|
| 大翔 | 技术部 |
| 白歌 | 技术部 |
结果解读:IN 匹配列表中的任意一个值,等价于 dept = '技术部'。当前表中所有员工都在技术部。
执行语句(NOT IN):
SELECT emp_name, dept FROM employees WHERE dept NOT IN ('技术部');
操作后结果:
| emp_name | dept |
|---|
结果解读: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 NULLNOT IN列表中不可包含 NULL,否则结果永远为空- 字符串比较是否区分大小写由排序规则决定
BETWEEN是闭区间,范围查询推荐用>= AND <模式
下一章引子:单个比较条件往往不够,需要把多个条件组合起来——这就是逻辑运算符的用武之地。