筛选行:完整的 WHERE

WHERE 用来保留符合条件的行。本课会讲多个条件的优先级、范围与模式匹配,以及 NULL 的处理方式。

组合条件

AND 留下两边都为真的行,OR 留下任一为真的行。写出来,这是在问:已支付或已发货、并且金额超过 500 的订单。

1

SELECT order_number, status, total
FROM orders
WHERE (status = 'paid' OR status = 'shipped')
  AND total > 500
ORDER BY total DESC
LIMIT 20;
在 Planche 中运行

结果 — 共 20 行,这里是前 5 行

order_numberstatustotal
ORD-2025-002767shipped30906.66
ORD-2022-001901shipped28151.01
ORD-2023-001772paid26797.87
ORD-2024-001907shipped26648.91
ORD-2025-002847paid25837.57

浏览器会另开一个标签页打开 Planche,示例数据库已经载入,这条查询也已经放好。本页其他示例都会复用同一个标签页;在你按下 F9 之前,什么都不会执行。

括号会改变条件的组合方式。把同样的条件去掉括号后再运行一次:

2

-- The same words without the brackets. AND binds tighter than OR,
-- so this reads as: paid (any total) OR shipped-and-over-500.
SELECT order_number, status, total
FROM orders
WHERE status = 'paid' OR status = 'shipped' AND total > 500
ORDER BY total DESC
LIMIT 20;
在 Planche 中运行

结果 — 共 20 行,这里是前 5 行

order_numberstatustotal
ORD-2025-002767shipped30906.66
ORD-2022-001901shipped28151.01
ORD-2023-001772paid26797.87
ORD-2024-001907shipped26648.91
ORD-2025-002847paid25837.57

答案变了,因为 AND 比 OR 结合得更紧——就像 × 比 + 更紧一样。没有括号,数据库读到的是"已支付、金额不限,或者 已发货且超过 500",于是每一笔小额已支付订单也都回来了。

匹配若干个值中的一个

一串比较同一个列的 OR,正是 IN 该出场的地方。

3

SELECT order_number, status, total, ordered_at
FROM orders
WHERE status IN ('paid', 'shipped', 'delivered')
ORDER BY ordered_at DESC
LIMIT 20;
在 Planche 中运行

结果 — 共 20 行,这里是前 5 行

order_numberstatustotalordered_at
ORD-2025-002480shipped6333.772025-12-28 23:52:14
ORD-2025-000505delivered2276.512025-12-28 11:16:39
ORD-2025-001581delivered2645.062025-12-28 06:12:38
ORD-2025-000995delivered2584.022025-12-28 05:47:53
ORD-2025-003219delivered561.482025-12-28 03:20:37

status IN ('paid', 'shipped', 'delivered') 说的就是三个 OR 要说的话,而且只说了一遍。取反是 NOT IN——它有一个和 NULL 有关的锋利边角,会在子查询那一课单独用一节来讲。

区间

BETWEEN a AND b 是 >= a AND <= b 的简写,两端都包含。

4

SELECT order_number, ordered_at, total
FROM orders
WHERE ordered_at BETWEEN '2025-01-01' AND '2025-01-31 23:59:59'
ORDER BY ordered_at
LIMIT 20;
在 Planche 中运行

结果 — 共 20 行,这里是前 5 行

order_numberordered_attotal
ORD-2025-0034122025-01-01 00:18:522806.57
ORD-2025-0036162025-01-01 11:18:146955.62
ORD-2025-0015032025-01-01 23:43:345837.23
ORD-2025-0003172025-01-02 05:08:3215130.7
ORD-2025-0012982025-01-02 18:36:325045.04

模式

LIKE 按模式比较,其中 % 代表任意长度的字符,_ 代表恰好一个字符。

5

SELECT first_name, last_name, email
FROM customers
WHERE email LIKE '%@inbox.example'
ORDER BY last_name
LIMIT 20;
在 Planche 中运行

结果 — 共 20 行,这里是前 5 行

first_namelast_nameemail
FreyaAhmedfreya.ahmed@inbox.example
HugoAhmedhugo.ahmed@inbox.example
JonasAhmedjonas.ahmed@inbox.example
KenjiAhmedkenji.ahmed@inbox.example
LiamAhmedliam.ahmed@inbox.example

缺失的值

这些客户里有 279 位没有记录电话号码。就是这些人:

6

SELECT id, first_name, last_name, phone
FROM customers
WHERE phone IS NULL
LIMIT 20;
在 Planche 中运行

结果 — 共 20 行,这里是前 5 行

idfirst_namelast_namephone
2MateoCostaNULL
9SvenSmithNULL
21LaylaFischerNULL
25FreyaNguyenNULL
28PriyaJangNULL

而下面这条,是大多数人第一次会写的:

7

-- 279 customers have no phone number, and this finds none of them.
SELECT id, first_name, last_name, phone
FROM customers
WHERE phone = NULL;
在 Planche 中运行

没有匹配的行

零行。不是报错,也不是警告——就是一张空网格。

NULL 不是一个值,而是值的缺失,所以任何与它的比较都不可能为真。phone = NULL 问的是"这个未知的东西是否等于那个未知的东西",诚实的答案是"未知",而未知不等于真,于是这一行被丢掉。NULL = NULL 同样不为真。唯一管用的判断是 IS NULL 和 IS NOT NULL。

现在你能做什么

针对一张表,你几乎能表达任何条件。剩下的限制仍然是一切都在一张表里——而值得问的问题通常不在。

自己动手写

  1. 在韩国或日本、并且同意接收营销邮件(marketing_opt_in 为 1)的客户,先按国家、再按姓氏排序。

    查看答案

    查看答案

    SELECT first_name, last_name, country
    FROM customers
    WHERE country IN ('KR', 'JP')
      AND marketing_opt_in = 1
    ORDER BY country, last_name
    LIMIT 20;
    在 Planche 中运行

    结果 — 共 20 行,这里是前 5 行

    first_namelast_namecountry
    AishaAhmedJP
    IsabellaAhmedJP
    IsabellaBrownJP
    TheoBrownJP
    NoraChoiJP

    IN 省掉了一个 OR;这里只有一个 AND 连着两个条件,所以不需要括号。两个排序键让国家先起作用,姓氏在国家内部起作用。

  2. 2024 年内下的、最终被取消或退款的订单,最早的排在前面。

    查看答案

    查看答案

    SELECT order_number, status, ordered_at
    FROM orders
    WHERE ordered_at >= '2024-01-01'
      AND ordered_at <  '2025-01-01'
      AND status IN ('cancelled', 'refunded')
    ORDER BY ordered_at
    LIMIT 20;
    在 Planche 中运行

    结果 — 共 20 行,这里是前 5 行

    order_numberstatusordered_at
    ORD-2024-002563cancelled2024-01-02 11:41:56
    ORD-2024-002814cancelled2024-01-08 06:10:29
    ORD-2024-003123refunded2024-01-10 15:59:33
    ORD-2024-003024refunded2024-01-11 19:36:33
    ORD-2024-002258refunded2024-01-12 15:43:55

    重点在半开区间:>= '2024-01-01' AND < '2025-01-01' 覆盖了整年,包括 12 月 31 日的最后一刻,而 BETWEEN '2024-01-01' AND '2024-12-31' 会在零点就切断。