WHERE
导学
一张表可能有百万行数据,但你通常只关心其中满足特定条件的少数行。WHERE 子句就是 SQL 的"筛选器",它决定哪些行会被返回。
定义
WHERE:用于在 SELECT、UPDATE、DELETE 语句中指定过滤条件,只有满足条件的行才会被处理。WHERE 在 SQL 执行顺序中紧跟 FROM 之后。
核心语法
SELECT 列 FROM 表 WHERE 条件;
条件可以是:
- 比较运算:
=,<>,!=,>,<,>=,<= - 逻辑运算:
AND,OR,NOT - 范围判断:
BETWEEN ... AND ... - 集合判断:
IN (...) - 模糊匹配:
LIKE - 空值判断:
IS NULL,IS NOT NULL
演示数据准备
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 |
|---|---|
| 大翔 | 技术部 |
| 白歌 | 技术部 |
结果解读:只返回 dept = '技术部' 的行。字符串比较在 MySQL 中默认不区分大小写(取决于排序规则)。如需区分大小写,可使用 BINARY 关键字:WHERE BINARY dept = '技术部' 仍返回相同结果(因为本身大小写已匹配)。
场景二:范围查询(BETWEEN)
当前数据状态:见上文 employees 表完整数据。
执行语句:
SELECT emp_name, score FROM employees
WHERE score BETWEEN 80 AND 100;
操作后结果:
| emp_name | score |
|---|---|
| 大翔 | 100.00 |
结果解读:BETWEEN 是闭区间,包含两端边界。它等价于 score >= 80 AND score <= 100。共返回 1 行,白歌(score 为 NULL)被排除。
场景三:集合查询(IN)
当前数据状态:见上文 employees 表完整数据。
执行语句:
SELECT emp_name, dept FROM employees
WHERE dept IN ('技术部');
操作后结果:
| emp_name | dept |
|---|---|
| 大翔 | 技术部 |
| 白歌 | 技术部 |
结果解读:IN 等价于多个 OR 的简写:dept = '技术部'。当前表中只有技术部一个部门。当集合元素较多时,IN 比一长串 OR 更简洁。
场景四:排除查询(不等于)
当前数据状态:见上文 employees 表完整数据。
执行语句:
SELECT emp_name, score FROM employees
WHERE dept <> '技术部';
操作后结果:
| emp_name | score |
|---|
结果解读:<> 表示不等于,排除了大翔和白歌(技术部)。当前表中所有员工都在技术部,因此没有数据返回。
易错点:判断 NULL 不能用
= NULL或<> NULL,必须用IS NULL或IS NOT NULL。这是 SQL 标准规定的三值逻辑(真、假、未知)导致的。
场景五:NULL 判断(IS NULL / IS NOT NULL)
当前数据状态:见上文 employees 表完整数据,其中白歌的 score 为 NULL。
执行语句(错误写法):
-- 错误写法(永远返回空结果)
SELECT * FROM employees WHERE score = NULL;
操作后结果:空结果集(0 行)。
结果解读:任何值与 NULL 的比较结果都是 UNKNOWN,而 WHERE 只保留 TRUE 的行,因此没有任何行满足条件。
执行语句(正确写法):
SELECT emp_name, score FROM employees WHERE score IS NULL;
操作后结果:
| emp_name | score |
|---|---|
| 白歌 | NULL |
结果解读:IS NULL 是专门判断 NULL 的运算符,正确返回了 score 为 NULL 的白歌。
再看 IS NOT NULL:
SELECT emp_name, score FROM employees WHERE score IS NOT NULL;
操作后结果:
| emp_name | score |
|---|---|
| 大翔 | 100.00 |
结果解读:返回了所有 score 不为 NULL 的 1 行。
场景六:复合条件(AND / OR)
当前数据状态:见上文 employees 表完整数据。
执行语句:
SELECT emp_name, dept, score FROM employees
WHERE dept = '技术部' AND score >= 90;
操作后结果:
| emp_name | dept | score |
|---|---|---|
| 大翔 | 技术部 | 100.00 |
结果解读:AND 要求两个条件同时满足。技术部部门的员工有大翔、白歌,但白歌的 score 为 NULL(不满足 >= 90),因此只有大翔被返回。
再看一个 OR 的例子:
SELECT emp_name, dept, score FROM employees
WHERE dept = '产品部' OR score IS NULL;
操作后结果:
| emp_name | dept | score |
|---|---|---|
| 白歌 | 技术部 | NULL |
结果解读:OR 要求满足任一条件即可。白歌的 score IS NULL,满足条件,因此被返回。大翔的 dept 为技术部且 score 不为 NULL,不满足任一条件。
WHERE 与索引的关系
WHERE 条件是索引优化的核心入口。如果 WHERE 条件中的列有合适的索引,MySQL 可以直接定位到目标行;否则将进行全表扫描。
关于索引的详细内容,参见第05章《索引》文档。
常见误区
| 误区 | 正解 |
|---|---|
WHERE 1=1 无影响 | 虽然结果正确,但会让优化器难以处理,且显得业余。动态 SQL 应去掉多余的 AND。 |
| 字符串比较区分大小写 | MySQL 默认排序规则(如 utf8mb4_unicode_ci)不区分大小写。 |
WHERE column = NULL | 任何值与 NULL 的比较结果都是 UNKNOWN,必须用 IS NULL。 |
WHERE 中可以用 SELECT 别名 | 不行,WHERE 在 SELECT 之前执行,别名还未生成。 |
面试考点
Q:WHERE 和 HAVING 的区别?
WHERE在分组前过滤行,不能引用聚合函数;HAVING在分组后过滤组,可以引用SUM、COUNT等聚合函数。执行顺序:WHERE先于GROUP BY和HAVING。
Q:IN 和 OR 有什么区别?性能上哪个好?
逻辑上等价,但
IN更简洁。MySQL 5.7 中两者通常会被优化为相同的执行计划。当IN列表很长时,性能可能不如JOIN子查询。
Q:为什么 WHERE column = NULL 查不到数据?
SQL 使用三值逻辑:TRUE、FALSE、UNKNOWN。任何值与 NULL 的比较结果都是 UNKNOWN,而
WHERE只保留 TRUE 的行。必须使用IS NULL。
Q:如何查询"某列不等于某值"且包含 NULL 的行?
WHERE column <> '某值' OR column IS NULL。因为column <> '某值'对 NULL 返回 UNKNOWN,不会包含 NULL 行,必须显式加上OR column IS NULL。
小结
WHERE是行级过滤器,决定哪些行进入结果集BETWEEN是闭区间,IN是集合匹配NULL只能用IS NULL/IS NOT NULL判断WHERE条件是索引优化的关键入口WHERE中不能使用SELECT中定义的别名
下一章引子:筛选出了目标行,接下来学习如何按你想要的顺序呈现结果。