2. 使用WHERE子句实现多条件精确筛选

使用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'
WHERE 多条件筛选:逻辑分组与优先级 ❌ 缺少括号(优先级陷阱) WHERE dept='研发部' OR dept='产品部' AND salary > 12000 AND status <> '离职' AND hire_date > '2021-01-01' 实际解析:研发部 OR (产品部 AND 薪资 AND 状态 AND 日期) → 研发部全员被选中,薪资条件失效 ✅ 括号分组(业务意图明确) WHERE (dept='研发部' OR dept='产品部') AND salary > 12000 AND status <> '离职' AND hire_date > '2021-01-01' 先算括号内 OR,再与其余 AND 条件求交集 → 仅研发/产品部中薪资达标且在职者入选 逻辑优先级真值表(A=部门匹配, B=薪资达标, C=在职, D=日期达标) 表达式 A B C D 结果 A OR (B AND C AND D) T F F F T(误选) (A OR B) AND C AND D T F F F F(正确) (A OR B) AND C AND D T T T T T(正确) (A OR B) AND C AND D F T T T T(正确)

完整可执行 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_idemp_namedeptsalaryhire_datestatus
7孙丽产品部125002021-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 判断。

验证逻辑优先级是否生效,最直接的方法是对比“加括号”与“不加括号”两条语句的返回行数和具体记录,观察差异是否与真值表一致。