SQL 里的 AND / OR 优先级陷阱:一个括号引发的"数据越界"
一、背景:给 WHERE 加了个 OR,差点全表数据都出来了
在一次查询接口的调整中,原本的 WHERE 是这样:
sql
where w.warehouse_type = 3
and st.member_id = xxx
需求要额外支持"xxx仓"(warehouse_business_type = 2),有人顺手改成:
sql
where w.warehouse_type = 3 or w.warehouse_business_type = 2
and st.member_id = xxx
看起来只是"多查一种仓库类型",实际却是个严重 bug:member_id = xxx 这个限制对一部分数据失效了,会把别的客户、别的时间的数据也查出来------典型的"数据越界"。
根因就是 AND 和 OR 的运算优先级。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
二、核心概念:AND 优先级高于 OR
SQL 里逻辑运算符有明确的优先级顺序(从高到低):
NOT > AND > OR
也就是说,AND 会先于 OR 结合。当一个 WHERE 里同时有 AND 和 OR 又没加括号时,数据库会先把 AND 两边"绑在一起",再和 OR 组合。
回到那句 SQL,数据库实际是这样理解的(等价于自动加了括号):
sql
where (w.warehouse_type = 3)
OR (w.warehouse_business_type = 2 AND st.member_id = xxxx)
拆开看这两个分支:
- 分支1:
w.warehouse_type = 3------ 完全不带 member_id 条件,只要是 type=3 的仓,所有客户、所有时间的数据都会被选出来 - 分支2:
w.warehouse_business_type = 2 AND st.member_id = 226887------ 这个才带了客户限制
结果:本该"限定 xxx这个客户"的查询,在 type=3 分支上彻底失控。这在生产上意味着数据泄露 + 慢查询(全表扫描)。
正确写法:用括号明确意图
sql
where (w.warehouse_type = 3 or w.warehouse_business_type = 2)
and st.member_id = xxx
加了括号后,两个仓库类型的 OR 被"锁"在一起,整体再和 member_id = xxx 做 AND。这样无论哪种仓库类型,都必须满足 member_id = xxxx,意图清晰、结果正确。
一句话:只要 WHERE 里 AND 和 OR 混用,就应该用括号把 OR 显式括起来,不要依赖默认优先级。
三、通用示例场景
示例1:查询商品------"电子类 或 图书类,且库存大于 0"
sql
-- 错误:没加括号
SELECT * FROM product
WHERE category = '电子' OR category = '图书' AND stock > 0;
-- 实际被理解成:
-- category='电子' (不管库存)
-- OR (category='图书' AND stock>0)
-- 结果:库存为 0 的电子产品也被查出来了
-- 正确:加括号
SELECT * FROM product
WHERE (category = '电子' OR category = '图书') AND stock > 0;
示例2:用户筛选------"VIP 或 高消费用户,且账号未封禁"
sql
-- 错误
SELECT * FROM users
WHERE is_vip = 1 OR total_amount > 10000 AND status = 'active';
-- 被理解为:is_vip=1(含被封禁的VIP)OR (高消费 AND 未封禁)
-- 后果:被封禁的 VIP 也被查出来
-- 正确
SELECT * FROM users
WHERE (is_vip = 1 OR total_amount > 10000) AND status = 'active';
示例3:日志查询------"错误 或 警告级别,且属于今天"
sql
-- 正确写法
SELECT * FROM log
WHERE (level = 'ERROR' OR level = 'WARN')
AND create_time >= '2026-09-18 00:00:00';
如果不加括号,level='ERROR' 会脱离时间限制,把历史所有 ERROR 日志都拉出来。
示例4:用 IN 替代多个 OR,更清晰也更安全
上面"多个 OR 判断同一个字段"的场景,用 IN 会更简洁,还天然避免优先级问题:
sql
-- 用 IN 代替 category='电子' OR category='图书'
SELECT * FROM product
WHERE category IN ('电子', '图书') AND stock > 0;
IN 本身是一个整体条件,和后面的 AND 组合时不存在优先级歧义。能用 IN 就别堆 OR。
四、容易踩的坑
坑1:OR 混进一堆 AND 里,最隐蔽
WHERE 条件多的时候,一个 OR 夹在中间最容易出事:
sql
-- 危险:a、b、d 都是限制条件,中间冒出一个 or c
WHERE a = 1 AND b = 2 OR c = 3 AND d = 4;
-- 实际 = (a=1 AND b=2) OR (c=3 AND d=4)
条件越多,默认优先级带来的结果越难一眼看出。规则:只要有 OR,就给 OR 那组加括号。
坑2:OR 让索引失效、引发全表扫描
优先级错误还常常伴随性能问题。像 WHERE type=3 OR business_type=2:
- 如果两个列各有索引,MySQL 有时能用 index merge,但跨列 OR 经常用不上索引
- 更糟的是优先级错误让某个分支丢失了主过滤条件(如 member_id、时间),直接退化成全表扫描
优化方向:
- 同字段多值用
IN - 不同字段的 OR,若性能敏感,考虑用
UNION拆成两个能各自走索引的查询
sql
SELECT ... WHERE warehouse_type = 3 AND member_id = 226887
UNION
SELECT ... WHERE warehouse_business_type = 2 AND member_id = 226887;
坑3:OR 与 NOT 一起用
NOT 优先级最高,NOT a = 1 OR b = 2 等于 (NOT a=1) OR b=2,不是 NOT (a=1 OR b=2)。同样靠括号消除歧义。
坑4:JOIN 的 ON 条件里也有同样问题
不只是 WHERE,JOIN 的 ON 子句里 AND/OR 混用也遵循同样优先级,一样要加括号:
sql
LEFT JOIN t2 ON (t1.a = t2.a OR t1.b = t2.b) AND t2.status = 1
五、运算符优先级速查(常用部分)
从高到低:
| 优先级 | 运算符 |
|---|---|
| 高 | () 括号 |
| ↑ | 算术:* / %,+ - |
| ↑ | 比较:= > < >= <= != <>,IN、LIKE、BETWEEN、IS |
| ↑ | NOT |
| ↑ | AND |
| 低 | OR |
记住核心一条:AND 比 OR 先算。其余细节记不住没关系,加括号永远是对的。
六、总结
| 要点 | 说明 |
|---|---|
| 优先级 | NOT > AND > OR,AND 先于 OR 结合 |
| 主要风险 | 无括号的 OR 会让部分 AND 条件失效 → 数据越界 / 数据泄露 |
| 黄金法则 | WHERE/ON 里 AND、OR 混用,一律给 OR 那组加括号 |
| 简化技巧 | 同字段多值用 IN;性能敏感的跨列 OR 考虑用 UNION 拆分 |
| 验证手段 | 用 EXPLAIN 看是否全表扫描;用少量数据核对结果集是否符合预期 |