CodeGym /课程 /SQL SELF /使用JOIN时常见的错误

使用JOIN时常见的错误

SQL SELF
第 12 级 , 课程 4
可用

现在是时候聊聊现实了。生活总有避不开的那一面:错误。抓bug、修bug、理解bug——这就是搞数据的日常。来看看在SQL里用 JOIN 时都有哪些坑,以及怎么绕过去。

错误1:漏写连接条件——生成笛卡尔积

最常见的错误就是——忘了写 ON 连接条件。这时候就会出现笛卡尔积,也就是第一张表的每一行都和第二张表的每一行拼在一起。结果就是一堆没意义的行,看着都头大。

举个例子。假设我们有下面两张表:

学生 (students):

student_id name
1 Otto
2 Anna

课程 (courses):

course_id course_name
101 数学
102 历史

现在我们写个查询,忘了 ON

SELECT *
FROM students
JOIN courses;

结果:

student_id name course_id course_name
1 Otto 101 数学
1 Otto 102 历史
2 Anna 101 数学
2 Anna 102 历史

这看着就不对劲吧?这个噩梦就叫 笛卡尔积

怎么修:ON 指定表之间的关联条件。

SELECT *
FROM students
JOIN courses
ON students.student_id = courses.course_id;

然后又有新坑了……

防呆机制

这个问题太常见了,所以PostgreSQL直接禁止你用 JOIN 却不写 ON 和条件。

如果你真想每行都拼每行,可以不用 JOIN,直接这样写:

SELECT *
FROM students, courses;

还有第3种情况——什么时候 JOIN 不用 ON 也能用

  • NATURAL JOIN —— 自动找名字一样的列来连。
  • USING —— 你指定哪些列来连。
  • CROSS JOIN —— 永远不用条件,就是笛卡尔积。

错误2:连接条件写错了

有时候你写了连接条件,但写错了。比如不是用主键连外键,而是用了一些八竿子打不着的字段。

比如我们想查学生和他们选的课程,但写错了,把表连在了不相关的字段上:

SELECT *
FROM students
JOIN courses
ON students.student_id = courses.course_id;

这个查询结果肯定不对,因为 student_idcourse_id 完全不是一回事。

怎么修:一定要用对的列来连。正确的写法应该是(假如你有个 enrollments 表,专门连学生和课程):

SELECT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;

错误3:结果里有重复行

你在查询里加了好几个 JOIN,有时候就会出现重复行。这通常是因为 JOIN 的表里有重复数据,或者你连的条件写得不对。

比如,学生Otto在 enrollments 表里同一个课程被登记了两次。

enrollments 里的记录:

student_id course_id
1 101
1 101

现在用 JOIN 查出来会这样:

SELECT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;

结果:

name course_name
Otto 数学
Otto 数学

怎么修:第一,确保表里没有重复数据。第二,如果这种重复是正常的,那就用 DISTINCT 去重:

SELECT DISTINCT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;

错误4:用 INNER JOIN 时丢了行

INNER JOIN 只会返回两张表都能对上的行。如果有一张表里没有对应的值,这行就直接被扔掉了。选错连接类型就会丢数据。

比如,有个学生还没选任何课程:

学生 (students):

student_id name
1 Otto
2 Anna
3 Dhany

登记 (enrollments):

student_id course_id
1 101
2 102

现在用 INNER JOIN 查:

SELECT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;

结果:

name course_name
Otto 数学
Anna 历史

那Dhany去哪了?如果你想把没选课的学生也查出来,就得用 LEFT JOIN

SELECT students.name, courses.course_name
FROM students
LEFT JOIN enrollments ON students.student_id = enrollments.student_id
LEFT JOIN courses ON enrollments.course_id = courses.course_id;

错误5:NULL 值处理不对

如果有表里有 NULL(空值),它们可能会被过滤掉(比如你加了过滤条件)。

比如:你用了 LEFT JOIN,但又加了 WHERE 过滤。

SELECT students.name, courses.course_name
FROM students
LEFT JOIN enrollments ON students.student_id = enrollments.student_id
LEFT JOIN courses ON enrollments.course_id = courses.course_id
WHERE courses.course_name = '数学';

这样没选课的学生就不会出现在结果里了,哪怕你用了 LEFT JOIN

怎么修:如果你想把没选课的学生也查出来,要么把 WHERE 换成 ON,要么加个条件:

SELECT students.name, courses.course_name
FROM students
LEFT JOIN enrollments ON students.student_id = enrollments.student_id
LEFT JOIN courses ON enrollments.course_id = courses.course_id
WHERE courses.course_name IS NULL OR courses.course_name = '数学';

错误6:搞混了连接类型

你可能会搞不清该用哪种连接。比如本来可以用 LEFT JOIN,结果用了 RIGHT JOIN,其实只要换下表的顺序就行了。

怎么避免混乱:

  • 能用 LEFT JOIN 就用,这样更直观。
  • 换下表的顺序,就不用 RIGHT JOIN 了。
2
任务
SQL SELF, 第 12 级, 课程 4
已锁定
修正笛卡尔积
修正笛卡尔积
2
任务
SQL SELF, 第 12 级, 课程 4
已锁定
使用 `LEFT JOIN` 避免数据丢失
使用 `LEFT JOIN` 避免数据丢失
1
调查/小测验
多重 JOIN第 12 级,课程 4
不可用
多重 JOIN
多重 JOIN
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION