讲解

内连接会丢掉匹配不上的行,但很多问题的出发点恰恰是「全部都要」:列出所有学生及其订单数(包括没下过单的孙丽)、所有课程及销量(包括没人买的 Go 语言入门)。LEFT JOIN 保留左表的全部行,右表配不上就用 NULL 补齐;RIGHT JOIN 正好相反,保留右表全部行。左表右表就是 FROM 和 JOIN 关键字两侧的那两张表。

RIGHT JOIN 能做的事,换个方向用 LEFT JOIN 都能做:A RIGHT JOIN B 等价于 B LEFT JOIN A。社区主流习惯是只用 LEFT JOIN,查询读起来始终是「以左表为主」。SQLite 直到 3.39 才支持 RIGHT JOIN,早些年的版本根本没有,本教程环境已支持,下面的示例会演示,但日常写 SQL 建议统一用 LEFT JOIN。

外连接的经典用法是「找缺失」:LEFT JOIN 后检查右侧连接键 IS NULL,就能找出没有任何订单的学生、没人购买的课程。另一个大坑是 WHERE 子句的位置:对右表列的筛选写在 WHERE 里,会把右表补 NULL 的行再过滤掉,LEFT JOIN 悄悄退化成了 INNER JOIN;这种条件应该写进 ON 里。

示例

所有学生的订单数,没下过单的学生显示 0(孙丽的订单数为 0):

SELECT s.name, COUNT(o.id) AS order_count FROM students AS s LEFT JOIN orders AS o ON s.id = o.student_id GROUP BY s.id, s.name ORDER BY order_count DESC, s.id;

找缺失:没有任何订单的学生、没人买过的课程:

SELECT s.name FROM students AS s LEFT JOIN orders AS o ON s.id = o.student_id WHERE o.id IS NULL;

SELECT c.title FROM courses AS c LEFT JOIN orders AS o ON c.id = o.course_id WHERE o.id IS NULL;

RIGHT JOIN 演示(与 LEFT JOIN 互换两侧等价):以订单为主,学生不存在时学生列为 NULL(12 号订单的 student_id 是 9):

SELECT o.id, s.name, o.amount FROM students AS s RIGHT JOIN orders AS o ON s.id = o.student_id WHERE s.id IS NULL;

常见坑

  • 右表筛选条件写错位置:LEFT JOIN 后在 WHERE 里写 o.status = 'paid',会把右表为 NULL 的行全部滤掉,结果和内连接一模一样。要保留「无条件左表行」,把这个条件移到 ON 里。
  • 数错订单数:LEFT JOIN 后 COUNT(*) 会把补 NULL 的行也数成 1,「孙丽有 1 条订单」就错了。统计右表行数要用 COUNT(o.id),它会跳过 NULL。
  • 对右表列判断等值而不是 IS NULL:找缺失时写 WHERE o.id = NULL 永远查不到,必须用 IS NULL——NULL 章节的规则在这里同样适用。
  • 滥用 RIGHT JOIN:RIGHT JOIN 让读者要倒过来想「以谁为主」,团队里统一只用 LEFT JOIN 能减少大量理解成本。

小结

LEFT JOIN 保留左表全部行、右表失配补 NULL;RIGHT JOIN 反之且可改写为 LEFT JOIN;「找缺失」用右侧键 IS NULL;右表条件放 ON 里。下一节学习子查询。