用 LEFT JOIN 保留一张表的每一行,用 IS NULL 发现缺失,并用 COALESCE 替换它们。
在 Kodokon 中打开本课上一课的 INNER JOIN 会隐藏没有订单的客户。然而,最常见的业务问题恰恰是:谁缺失了? 不活跃的客户、从未售出的产品、没有付款的发票…… 请在 sqliteonline.com 或 sqlite3 中运行这段脚本:它会新增第四个没有订单的客户。
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
city TEXT
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
amount REAL NOT NULL
);
INSERT INTO customers (id, name, city) VALUES
(1, 'Alice', 'Lyon'),
(2, 'Karim', 'Paris'),
(3, 'Mina', 'Nantes'),
(4, 'Paulo', 'Lille');
INSERT INTO orders (id, customer_id, amount)
VALUES
(1, 1, 49.90),
(2, 1, 15.00),
(3, 2, 120.50);LEFT JOIN 会保留左表(写在关键字之前的那张表)的所有行,即使右侧没有匹配。当匹配缺失时,右表的各列会被填上 NULL。
SELECT c.name, o.id AS order_id, o.amount
FROM customers AS c
LEFT JOIN orders AS o
ON o.customer_id = c.id;为了单独找出缺失的部分,就对这些 NULL 值进行过滤:这就是反连接(anti-join)模式,是数据分析中一个必备的本能反应。被检测的列必须来自右表,并且本身绝不应自然为 NULL,因此选用它的主键 o.id。
SELECT c.name, c.city
FROM customers AS c
LEFT JOIN orders AS o
ON o.customer_id = c.id
WHERE o.id IS NULL;最后一个工具:COALESCE(a, b) 在 a 不为 NULL 时返回 a,否则返回 b。把它与 LEFT JOIN 和聚合结合起来,就能在报表或发票中把缺失转化为可用的零。
SELECT c.name,
COALESCE(SUM(o.amount), 0) AS total_spent
FROM customers AS c
LEFT JOIN orders AS o
ON o.customer_id = c.id
GROUP BY c.id
ORDER BY total_spent DESC;