表连接
真实查询几乎都是多表 Join。PG 支持完整的连接类型,外加一个非常强但常被忽视的 LATERAL 连接。
1. INNER JOIN:只保留两边都匹配的行
SELECT o.order_no, u.username, o.total_amount
FROM core.orders o
JOIN core.users u ON u.id = o.user_id;不满足 ON 条件的行都会被丢弃。
2. LEFT / RIGHT / FULL JOIN
LEFT JOIN:左表全保留,右表无匹配时补 NULL(最常用)。RIGHT JOIN:右表全保留(通常用LEFT JOIN调换顺序替代,可读性更好)。FULL JOIN:两边全保留,哪边没匹配哪边补 NULL。
SELECT u.username
FROM core.users u
LEFT JOIN core.orders o ON o.user_id = u.id
WHERE o.id IS NULL; -- 右表为 NULL = 没有匹配到订单💡LEFT JOIN + IS NULL 反查
「A 里没有对应 B 的记录」是经典需求(如未下单用户、未评论商品),写法是 LEFT JOIN ... WHERE B.key IS NULL。
3. USING 与多表连接
当连接列的列名相同时可用 USING 简化:
SELECT o.order_no, p.name, oi.quantity
FROM core.orders o
JOIN core.order_items oi USING (order_id)
JOIN core.products p USING (id); -- 注意 oi 和 p 通过 product_id 关联,这里示意 USING 用法三表及以上连接时,注意连接顺序和过滤条件位置(尽早过滤减少中间结果)。
4. 自连接:一张表当两张用
分类表 categories 里 parent_id 指回自己,要展平「父→子」就自连接:
SELECT parent.name AS 父分类, child.name AS 子分类
FROM core.categories child
JOIN core.categories parent ON parent.id = child.parent_id;5. LATERAL:子查询能引用左边表的列(PG 特色)
普通子查询不能引用外层列。LATERAL(常配合 LEFT JOIN)允许右侧子查询/函数引用左侧每行的值——非常适合「对每一行取 Top N 关联记录」:
SELECT u.username, recent.order_no, recent.created_at
FROM core.users u
LEFT JOIN LATERAL (
SELECT order_no, created_at
FROM core.orders o
WHERE o.user_id = u.id
ORDER BY o.created_at DESC
LIMIT 3
) recent ON true;没有 LATERAL 时,这种「每组取前 N」得用窗口函数(第 11 章)或复杂子查询;LATERAL 表达得最直白。注意必须 LEFT JOIN LATERAL ... ON true,否则没匹配时整行会被丢弃。
ℹ️LATERAL 的等价思路
「每组取前 N」既可用 LATERAL,也可用窗口函数 ROW_NUMBER() 分区排序后取 rn<=3(见第 11 章)。简单 Top-N 用 LATERAL 更直观,复杂分析用窗口函数更灵活。
🎯动手
用 LEFT JOIN 查出所有商品及其被下单的总数量(没被下单的商品数量显示为 0),只列出总数量大于 0 的商品,按数量降序。