想象一下,你要合并两张表:students(学生)和enrollments(课程报名)。如果enrollments表里没有某个学生的信息,但你用了比如LEFT JOIN,那students表的行还是会出来,但enrollments的信息就没了。这种情况下就会出现NULL。
大概长这样:
表students:
| id | name |
|---|---|
| 1 | Eva |
| 2 | Peter |
| 3 | Anna |
表enrollments:
| student_id | course_name |
|---|---|
| 1 | 数学 |
| 1 | 信息学 |
| 2 | 物理 |
用LEFT JOIN的查询:
SELECT students.id, students.name, enrollments.course_name
FROM students
LEFT JOIN enrollments ON students.id = enrollments.student_id;
结果:
| id | name | course_name |
|---|---|---|
| 1 | Eva | 数学 |
| 1 | Eva | 信息学 |
| 2 | Peter | 物理 |
| 3 | Anna | NULL |
哈喽,NULL!你看,Anna没报任何课程,课程信息就没了,显示出来就是NULL。
NULL对查询有啥影响?
NULL不是“0”也不是“空字符串”,它是没有值。这种行为有点意思(有时候还挺烦人的):
和NULL比较:
如果你写WHERE course_name = NULL,查不出来NULL的行。为啥?因为NULL不能直接比较。
要判断是不是NULL,得用特殊操作符:
WHERE course_name IS NULL
数学运算:
任何和NULL的运算结果都是NULL。比如:
SELECT 5 + NULL; -- 结果: NULL
聚合函数:
大多数聚合函数,比如SUM()、AVG(),会忽略NULL,但COUNT(*)会把它们当作“有这行”。
怎么搞定NULL?
- 用
COALESCE()把NULL换成更友好的值
COALESCE()函数可以把NULL替换成别的值。比如课程没了,可以写"没有课程":
SELECT
students.id,
students.name,
COALESCE(enrollments.course_name, '没有课程') AS course_name
FROM
students LEFT JOIN enrollments
ON students.id = enrollments.student_id;
结果:
| id | name | course_name |
|---|---|---|
| 1 | Eva | 数学 |
| 1 | Eva | 信息学 |
| 2 | Peter | 物理 |
| 3 | Anna | 没有课程 |
现在看起来舒服多了吧?
- 过滤
NULL值
如果你不想看到NULL的行,可以用WHERE ... IS NOT NULL。比如:
SELECT
students.id,
students.name,
enrollments.course_name
FROM
students LEFT JOIN enrollments
ON students.id = enrollments.student_id
WHERE
enrollments.course_name IS NOT NULL;
结果:
| id | name | course_name |
|---|---|---|
| 1 | Eva | 数学 |
| 1 | Eva | 信息学 |
| 2 | Peter | 物理 |
Anna就没了,因为她没有任何课程记录。
- 统计时考虑
NULL:COUNT例子
前面说过,有些函数会忽略NULL,有些不会。比如:
要统计所有行,包括NULL:
SELECT COUNT(*) FROM students; -- 统计所有行(包括`course_name` = NULL的)
只统计没有NULL的行:
SELECT COUNT(course_name) FROM enrollments;
- 用
CASE写条件表达式
如果你不喜欢COALESCE()或者想更灵活点,可以用CASE。比如:
SELECT
students.id,
students.name,
CASE
WHEN enrollments.course_name IS NULL THEN '没有课程'
ELSE enrollments.course_name
END AS course_name
FROM
students LEFT JOIN enrollments
ON students.id = enrollments.student_id;
结果和用COALESCE()一样,但CASE能写更复杂的规则。
- 如果你确定不会有
NULL,用INNER JOIN
最极端的办法就是根本不让NULL出现,直接用INNER JOIN。这种JOIN只返回两张表都有匹配的行:
SELECT
students.id,
students.name,
enrollments.course_name
FROM
students INNER JOIN enrollments
ON students.id = enrollments.student_id;
没有意外——只有报了课程的学生。
结果:
| id | name | course_name |
|---|---|---|
| 1 | Eva | 数学 |
| 1 | Eva | 信息学 |
| 2 | Peter | 物理 |
如果你需要显示所有数据,包括NULL,那INNER JOIN就不适合,但有时候这就够了。
GO TO FULL VERSION