where 是筛行的那道闸:比较运算符六个、逻辑运算符三个、
还有 between/in/like/is null 四件常用工具。
Demo 撞了两件事:phone = NULL 给 0 行(NULL 和任何值比都得「未知」);
a or b and c 不加括号给 9 行、加括号给 3 行——and 比 or 高,这一条能让结果差三倍。
上一篇挑列,这一篇挑行。 where 是 SQL 里最容易写错也最常写错的子句。
它的位置固定在 from 之后、group by 之前,
这个顺序决定了它看不见聚合函数的结果(那是 having 的活)。
一、六个比较运算符
= 等于(注意只有一个等号,== 在 SQL 里不是运算符)、<> 不等于(也可写 !=)、
>、>=、<、<=。
字符串也能比大小:按校对规则(collation)逐字符比,所以 'abc' < 'abd' 为真。
日期也能比——前提是它存成了标准格式:'2026-04-01' < '2026-05-01' 字典序恰好等于时间序。
这也是为什么日期要存 YYYY-MM-DD。
二、逻辑运算符:and、or、not
and 要求两边都真、or 只要一边真、not 取反。
not 只作用于紧跟它的那个条件:not a = 1 and b = 2 是 (not a = 1) and (b = 2),
不是 not (a = 1 and b = 2)。
and 和 or 一混,就必须加括号。 这不只是可读性问题,
不加括号结果就是错的(下一篇的 Demo 就差三倍)。
三、四件常用工具:between、in、like、is null
between a and b 是闭区间,两头都算,等价于 >= a and <= b。
注意它不能反过来写(between 170 and 160 恒假)。
in (a, b, c) 是「等于其中任意一个」,
比一串 or 好读,而且数据量大时更快(内部会排序后二分)。
like 是模糊匹配(% 任意多个字符、_ 一个字符),第 5 章细讲。
is null 判断空值——不能写成 = null。
为什么 = null 永远不成立:
NULL 的意思是「未知」,「未知等于未知」的结果还是「未知」,不是真。
所以只能用 is null / is not null。
四、优先级:and 比 or 高
从高到低:括号 → 算术 → 比较(含 is/in/like/between)→ not → and → or。
所以 a or b and c 会被理解成 a or (b and c),
而不是 (a or b) and c。 这两者的结果可能差好几倍。
Demo 的数:gender='女' or gender='男' and height>175 给 9 行,加括号变 3 行。
9 行 = 全部 6 个女生 + 3 个高于 175 的男生。
五、还有两个隐式转换的坑
类型不一致时引擎会偷偷转换:where phone = 13800001111 里 phone 是字符串,
MySQL 会把它转成数字比,索引直接失效,而且可能匹配到意料之外的行。
'100' = 100 在 MySQL 里为真(转成数字),
但在严格比较的引擎里可能为假。 所以值该加引号就加引号,别指望引擎猜。
这条也是慢查询的常见原因:列是字符串、条件写数字,索引就用不上。
六、小结
教材小结:
where 用比较运算符、逻辑运算符与 between/in/like/is null 筛选行;and 的优先级高于 or。
本课补三条:一是 = null 永远不成立,只能用 is null;二是 and 与 or 混用一律加括号;
三是类型要对齐,别让引擎替你隐式转换(会废掉索引)。
七、动手 Demo:六个运算符、四个工具、一个优先级坑
Demo 六段:六个比较运算符逐个给行数,再测逻辑与四件工具,然后证明 = null 恒假,
最后对比加不加括号的差异。
这一篇的数都是 12 个学生的真实分布,可以自己心算核对。
import _q46 as Q
c = Q.con()
# 六个比较运算符
for cond in ["gender = '女'", "gender <> '女'", "height > 175",
"height >= 174", "height < 160", "height <= 160"]:
c.execute("SELECT COUNT(*) FROM students WHERE " + cond)
# 逻辑与其它运算符
c.execute("SELECT COUNT(*) FROM students "
"WHERE gender = '女' AND height > 160")
c.execute("SELECT COUNT(*) FROM students WHERE height BETWEEN 160 AND 170")
c.execute("SELECT COUNT(*) FROM students WHERE class_id IN (1, 3)")
c.execute("SELECT COUNT(*) FROM students WHERE name LIKE '张%'")
c.execute("SELECT COUNT(*) FROM students WHERE phone IS NULL")
# = NULL 永远不成立
c.execute("SELECT COUNT(*) FROM students WHERE phone = NULL")
# 优先级:不加括号 9 行,加括号 3 行
c.execute("SELECT COUNT(*) FROM students WHERE gender = '女' "
"OR gender = '男' AND height > 175")
c.execute("SELECT COUNT(*) FROM students WHERE (gender = '女' "
"OR gender = '男') AND height > 175")
实跑输出:
--------------------------------------------------------------------
第十一课:where 的运算符,以及优先级那个坑
--------------------------------------------------------------------
比较运算符:六个,等于只有一个等号
= gender = '女' -> 6 行 等于,注意是一个等号
<> gender <> '女' -> 6 行 不等于,也可写 !=
> height > 175 -> 3 行 大于
>= height >= 174 -> 4 行 大于等于
< height < 160 -> 1 行 小于
<= height <= 160 -> 1 行 小于等于
**`=` 是判断相等,`==` 在 SQL 里不是运算符**——写惯了别的语言最容易错这一处。
逻辑运算符:and、or、not
gender = '女' AND height > 160 -> 5 行
gender = '女' OR height > 175 -> 9 行
NOT gender = '女' -> 6 行
**`not` 只作用于紧跟它的那个条件**,所以 `not a = 1 and b = 2` 是 `(not a = 1) and (b = 2)`。
**加括号最稳。**
其他运算符:between、in、like、is null
height BETWEEN 160 AND 170 -> 6 行
class_id IN (1, 3) -> 8 行
name LIKE '张%' -> 1 行
phone IS NULL -> 1 行
**`between 160 and 170` 是闭区间,两头都算。** **`is null` 不能写成 `= null`**——
NULL 和任何值比较都得「未知」,永远筛不出来。
SQL> SELECT COUNT(*) AS with_eq_null FROM students WHERE phone = NULL
with_eq_null
-------------
0
(1 行)
**这一句给 0 行,就是「= NULL 永远不成立」的证据。**
优先级:and 比 or 高,这一条能坑死人
不加括号:9 行
加括号: 3 行
**不加括号时,引擎先算 `gender = '男' AND height > 175`,再和前半段 or 起来——**
**所以它等于「所有女生」加「身高超过 175 的男生」,而不是你要的「所有人都高于 175」。**
**规矩只有一条:where 里同时出现 and 和 or,一律加括号。**
优先级从高到低,记这四层就够
1. 括号
2. 算术运算(+ - * /)
3. 比较运算(= <> > < >= <=)与 IS、IN、LIKE、BETWEEN
4. NOT → AND → OR
第一段六个比较。 女生 6 个、男生 6 个;
> 175 给 3 行、>= 174 给 4 行——差的那一行正好是 174.0 的周涛。
第二段逻辑。 女 and >160 给 5 行、女 or >175 给 9 行、not 女 给 6 行。
第三段四件工具。 between 6 行、in 8 行、like 1 行、
is null 1 行——孙悦没填手机号,所以 is null 命中她一个。
第四段 = null。 给 0 行,这就是「NULL 比较恒为未知」的证据。
第五段优先级。 不加括号 9 行、加括号 3 行,同一句话,两种意思。
第六段优先级表。 四层,够用了。
30 秒实验
height between 160 and 170与height >= 160 and height <= 170结果一样吗?- (先想再跑)
where phone is not null给几行? - 把
not gender = '女'改成not (gender = '女'),结果变吗?
常见误区
- 写
where 列 = null。 永远 0 行,而且不报错,最难查。 - and 与 or 混用不加括号。 结果悄悄错,不报错,靠对数才发现。
- 字符串列拿数字比。 隐式转换让索引用不上,还会匹配到意料之外的行。
自测三问(点开对答案)
1. 为什么 `= null` 永远筛不出行? —— NULL 表示「未知」,与任何值比较的结果都是「未知」,不是真; 判断空值只能 is null/is not null。 2. and 与 or 哪个优先级高?不加括号会怎样? —— and 更高。`a or b and c` 会被算成 `a or (b and c)`,和 `(a or b) and c` 结果可能差很多。 3. between 是闭区间还是开区间? —— 闭区间,两头都算,等价于 `>= 小 and <= 大`;反过来写恒为假。行筛出来了,但出来的顺序是随机的。 下一篇 order by:怎么排,以及中文到底按什么排。
- ☐ (本篇完,下一篇 3.4 排序子句:order by)