WHERE:条件过滤
本教程共 50 篇 · 第 16 篇 · 更新于 2026-07-31
16. WHERE:条件过滤
本节目标:学完本章你能用 WHERE 子句从结果里筛出符合条件的行,掌握各种比较条件,以及用
IS NULL/IS NOT NULL正确处理空值这个特殊家伙。
为什么需要 WHERE
上一章我们学会了 SELECT,但 SELECT * FROM 表 会一把抓出所有行。现实中你几乎从不需要”全部”——你要的是”年龄大于 30 的用户""邮箱为空的联系人""金额超过 100 的订单”。这时候就要在 SELECT 后面挂一个 WHERE 子句,告诉 SQLite:“只把满足条件的行放进结果集。”
WHERE 的本质是行级过滤器:它对原表的每一行做一次判断,判断为”真(true)“的行留下,判断为”假”或”未知”的行丢弃。最终留下的行,才进入结果集、才被展示。
NoteWHERE 不仅用在 SELECT 里。后面要学的
UPDATE(改数据)、DELETE(删数据)同样靠 WHERE 来圈定”改/删哪些行”。所以本章学会的过滤逻辑,是后面好多章的通用基本功。
WHERE 放在哪、长什么样
WHERE 紧跟在 FROM 表名 之后:
SELECT 列名 FROM 表名 WHERE 条件;
条件的基本形状是”左边表达式 比较运算符 右边表达式”:
SELECT name, age FROM users WHERE age > 30;
sqlite> SELECT name, age FROM users WHERE age > 30;
name age
----- ---
王五 41
这条语句的执行过程是:SQLite 先打开 users 表,逐行看 age 是否大于 30,只有王五(41)满足,于是结果集里只剩王五这一行。
比较运算符全家福
WHERE 里最常用的是比较运算符(Comparison Operators),用来判断两个值的大小关系:
| 运算符 | 含义 | 示例 |
|---|---|---|
= | 等于 | age = 30 |
<> 或 != | 不等于 | age <> 30 |
< | 小于 | age < 30 |
> | 大于 | age > 30 |
<= | 小于等于 | age <= 30 |
>= | 大于等于 | age >= 30 |
举几个实用例子(基于全局示例表 users):
-- 等于:找出年龄刚好 28 的人
SELECT name FROM users WHERE age = 28;
-- 不等于:找出年龄不是 28 的人
SELECT name, age FROM users WHERE age != 28;
-- 区间:年龄介于 25 到 40 之间(含两端)
SELECT name, age FROM users WHERE age >= 25 AND age <= 40;
Tip比较时务必保证两边类型能对上:数字比数字、文本比文本。虽然 SQLite 有”类型亲和性”会做隐式转换(详见第 8、9 章),但拿文本和整数硬比,容易得到出乎意料的结果,也拖慢查询。养成”同类型比较”的习惯最稳妥。
NULL 是个特殊家伙:不能用 = 判断
这里有一条必须划重点的红线,初学者 90% 会栽跟头:NULL 表示”未知 / 不适用”,它不等于任何东西,连它自己都不等于。
看全局表,李四 的 email 是 NULL(未知)。如果你写:
-- 错误写法:永远查不到任何东西!
SELECT name FROM users WHERE email = NULL;
这条语句不会报错,但返回空结果。因为 email = NULL 这个表达式,SQLite 算出来的不是”真”也不是”假”,而是”未知(unknown)“,于是这行被过滤掉。NULL 和 NULL 比较,结果仍是未知,而非相等。
正确做法是用专门的 IS NULL:
-- 正确:找出邮箱未知(为空)的人
SELECT name FROM users WHERE email IS NULL;
sqlite> SELECT name FROM users WHERE email IS NULL;
李四
反过来,想找”邮箱已知”的人,用 IS NOT NULL:
SELECT name, email FROM users WHERE email IS NOT NULL;
Warning判断空值永远用
IS NULL/IS NOT NULL,绝不能用= NULL或!= NULL。= NULL不仅查不到,还静默失败、不报错,是极易藏 bug 的写法。记住口诀:空值判断用 IS,等于比较用 =,两者不能混。
多个条件怎么连:AND 与 OR(先睹为快)
有时一个条件不够,要”既要……又要……”或”要么……要么……”。WHERE 里用 AND(并且) 和 OR(或者) 把多个条件拼起来:
-- AND:年龄 >= 25 并且 工资 >= 65000(这里借用示例语义)
SELECT name, age FROM users WHERE age >= 25 AND age <= 40;
-- OR:年龄小于 25 或者 年龄大于 40
SELECT name, age FROM users WHERE age < 25 OR age > 40;
AND 要求所有条件都为真,整行才留下;OR 只要任一条件为真,整行就留下。逻辑运算符的完整规则(含 NOT、优先级、括号)我们放在第 17 章系统讲,这里你只需知道 WHERE 能这样组合条件即可。
比较时的”类型亲和性”陷阱
SQLite 是动态类型系统(详见第 8、9 章),列声明只是**类型亲和性(Type Affinity)**的建议。这会让比较偶尔出现反直觉的结果。比如 age 列声明为 INTEGER,但你插入时混进了文本 '30',由于亲和性它可能被转成整数存,也可能按文本比较——一旦按文本比,'30' 和 30 在比较运算符下未必相等,排序也会乱掉。
所以一条铁律:插入时就把类型搞对,比较时两边类型一致。别把”SQLite 很宽容”当成”可以乱来”的理由——宽松是给你便利,不是给你埋雷。
WHERE 也用在 UPDATE / DELETE 上(重要预告)
前面提过 WHERE 不止服务于 SELECT。它同样决定 UPDATE 改哪些行、DELETE 删哪些行。这里先给一句保命提醒,后面章节会展开:
Warning写 UPDATE 或 DELETE 时,如果忘了写 WHERE,会作用于全表!
UPDATE users SET age = 0;没有 WHERE,表里每一行的 age 都会变成 0;DELETE FROM users;没有 WHERE,整张表数据清空(且如果没开事务,难以撤销)。永远先想清楚”我要影响哪些行”,再下笔。可以养成习惯:先写SELECT * FROM 表 WHERE 条件确认范围,再把 SELECT 换成 UPDATE/DELETE。
适用场景小结
- 查”特定人群”:
WHERE age > 18、WHERE name = '张三' - 查”缺失信息”:
WHERE email IS NULL(找待补充的资料) - 查”异常数据”:
WHERE amount < 0(找出负金额的可疑订单) - 查”时间范围”:
WHERE created_at >= '2026-01-01'(注意日期在 SQLite 里是 TEXT 存储,按 ISO8601 字符串比较)
实战:把多个条件拼成”复合筛选”
真实查询很少只有一个条件。把比较、逻辑运算符组合起来,就能表达复杂意图。比如”找出年龄 25 岁以上、且邮箱已知(非 NULL)的用户”:
SELECT name, age, email
FROM users
WHERE age >= 25 AND email IS NOT NULL;
再比如”找出要么未成年、要么年龄超过 60 的人”(高频的”两端人群”查询):
SELECT name, age FROM users
WHERE age < 18 OR age > 60;
写复合条件时,记住第 17 章会讲的重点:不确定优先级就加括号。像 age < 18 OR age > 60 AND vip = 1 这种写法,SQLite 会按”先 AND 后 OR”去算,很可能不是你本意。把括号写清楚:(age < 18 OR age > 60) AND vip = 1,语义才 unambiguous。
Note关于日期再强调一次(纠偏红线):SQLite 没有 DATE / TIME / DATETIME / TIMESTAMP 类型,日期时间是用 TEXT(推荐 ISO8601 格式如
2026-07-31)、REAL(儒略日)或 INTEGER(Unix 秒)来存储的。所以WHERE created_at >= '2026-01-01'本质是在比较两个文本字符串,恰好 ISO8601 格式按字典序比较就等于按时间先后比较,非常方便。别指望 SQLite 有原生”日期类型”帮你做日期运算——那要靠日期函数(后续章节)来解析文本。
类比小结
WHERE 就像筛子的网眼:原表的所有行是掺着沙的米,WHERE 后面的条件是网眼大小,只有符合条件的米粒(行)能漏进结果集的簸箕里。等于、大于、小于这些比较运算符,是你在调”网眼形状”;而 IS NULL 是专门对付”那颗既不说有也不说无、只是标记’未知’的空壳”的特殊筛法——它不能用普通的等于号去量,必须用 IS NULL 这把专用卡尺。记住:空值不是 0、不是空字符串,它是”未知”,判断它只能用 IS。