2. 使用WHERE子句实现多条件精确筛选
业务条件拆解:从自然语言到布尔表达式
假设员工表 employees 包含字段:emp_id、emp_name、dept、salary、hire_date、status。现在需要筛选出满足以下复合条件的员工:
- 属于
研发部或产品部; - 薪资大于 12000;
- 状态不是
离职; - 入职日期在 2021-01-01 之后。
自然语言中的“或”和“且”必须显式转换为 OR 与 AND,并用括号明确分组,否则 SQL 的优先级规则会改变筛选结果。
AND / OR / NOT 的优先级与括号作用
SQL 中逻辑运算符优先级为:NOT > AND > OR。这意味着:
WHERE dept = '研发部' OR dept = '产品部' AND salary > 12000
会被解析为:
WHERE dept = '研发部' OR (dept = '产品部' AND salary > 12000)
研发部员工无论薪资多少都会被选中,这通常不是业务想要的结果。正确写法必须用括号把“部门属于研发部或产品部”作为一个整体:
WHERE (dept = '研发部' OR dept = '产品部')
AND salary > 12000
AND status <> '离职'
AND hire_date > '2021-01-01'
完整可执行 SQL 与验证
以下语句可直接在 SQLite / MySQL / PostgreSQL 中运行(字段类型按通用语法编写):
-- 建表与示例数据
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(50),
dept VARCHAR(30),
salary DECIMAL(10,2),
hire_date DATE,
status VARCHAR(10)
);
INSERT INTO employees VALUES
(1, '张伟', '研发部', 15000, '2020-06-15', '在职'),
(2, '李娜', '产品部', 11000, '2021-03-01', '在职'),
(3, '王强', '研发部', 9000, '2022-01-10', '在职'),
(4, '赵敏', '市场部', 13000, '2020-11-20', '在职'),
(5, '陈晨', '产品部', 14000, '2021-07-01', '离职'),
(6, '刘洋', '研发部', 18000, '2019-05-01', '在职'),
(7, '孙丽', '产品部', 12500, '2021-09-15', '在职');
-- 目标查询:研发部或产品部,薪资>12000,非离职,2021年后入职
SELECT emp_id, emp_name, dept, salary, hire_date, status
FROM employees
WHERE (dept = '研发部' OR dept = '产品部')
AND salary > 12000
AND status <> '离职'
AND hire_date > '2021-01-01';
预期结果(示例数据下):
| emp_id | emp_name | dept | salary | hire_date | status |
|---|---|---|---|---|---|
| 7 | 孙丽 | 产品部 | 12500 | 2021-09-15 | 在职 |
验证要点:
- 张伟(研发部,15000,2020-06-15)因入职日期不满足被排除;
- 刘洋(研发部,18000,2019-05-01)因入职日期不满足被排除;
- 陈晨(产品部,14000,2021-07-01)因状态为“离职”被排除;
- 李娜(产品部,11000)因薪资不达标被排除;
- 王强(研发部,9000)因薪资不达标被排除;
- 赵敏(市场部)因部门不匹配被排除。
NOT 的等价写法:
status <> '离职' 与 NOT status = '离职' 等价。当条件本身是复合表达式时,NOT 必须配合括号,例如 NOT (dept = '研发部' AND salary > 15000),否则 NOT 只作用于紧邻的单个条件。
NULL 陷阱:如果
status 或 hire_date 允许为 NULL,status <> '离职' 对 NULL 行返回 UNKNOWN,该行不会被选中。若业务上需要包含 NULL 行,应写成 (status <> '离职' OR status IS NULL)。
执行计划与索引提示
在 dept、salary、status、hire_date 上分别建立索引后,数据库优化器可能选择:
- 若
salary > 12000过滤性最强,优先走 salary 索引范围扫描; - 若
dept只有少数几个值,可能走 dept 索引再回表过滤其余条件; - 括号内的
OR条件若涉及不同列,可能触发 index merge 或全表扫描,需结合EXPLAIN判断。
验证逻辑优先级是否生效,最直接的方法是对比“加括号”与“不加括号”两条语句的返回行数和具体记录,观察差异是否与真值表一致。