第 57.1 节:处理包含 NULL 值的列
在 MySQL 和其他 SQL 方言中,NULL 值具有特殊属性。
考虑以下表,包含求职者、他们工作过的公司以及离职日期。NULL 表示该求职者仍在该公司工作:
CREATE TABLE example (
applicant_id INT,
company_name VARCHAR(255),
end_date DATE
);
数据示例:
| applicant_id | company_name | end_date |
|---|---|---|
| 1 | NULL | |
| 1 | Intech | 2013-01-31 |
| 2 | Woodworking.com | 2016-08-25 |
| 2 | NY Times | 2013-11-10 |
| 3 | NFL.com | 2014-04-13 |
你的任务是编写一个查询,返回 2016-01-01 之后的所有行,包括仍在公司工作的员工(end_date 为 NULL 的行)。
以下查询:
SELECT * FROM example WHERE end_date > '2016-01-01';
未能包含任何 NULL 值的行:
| applicant_id | company_name | end_date |
|---|---|---|
| 2 | Woodworking.com | 2016-08-25 |
根据 MySQL 文档,使用算术运算符(<、>、=、<>)进行比较时,如果任一操作数为 NULL,结果返回 NULL 而非布尔值 TRUE 或 FALSE。因此,end_date 为 NULL 的行既不大于 2016-01-01 也不小于 2016-01-01。
可以通过使用 IS NULL 关键字解决:
SELECT * FROM example WHERE end_date > '2016-01-01' OR end_date IS NULL;
结果:
| applicant_id | company_name | end_date |
|---|---|---|
| 1 | NULL | |
| 2 | Woodworking.com | 2016-08-25 |
当使用 MAX() 等聚合函数和 GROUP BY 子句时,处理 NULL 会变得更加复杂。如果任务是选择每个 applicant_id 的最近离职日期,以下查询似乎是合理的首次尝试:
SELECT applicant_id, MAX(end_date) FROM example GROUP BY applicant_id;
结果:
| applicant_id | MAX(end_date) |
|---|---|
| 1 | 2013-01-31 |
| 2 | 2016-08-25 |
| 3 | 2014-04-13 |
但考虑到 NULL 表示仍在公司工作,第一行结果是不准确的。使用 CASE WHEN 可以提供解决 NULL 问题的方法:
SELECT
applicant_id,
CASE WHEN MAX(end_date IS NULL) = 1 THEN 'present' ELSE MAX(end_date) END AS max_date
FROM example
GROUP BY applicant_id;
结果:
| applicant_id | max_date |
|---|---|
| 1 | present |
| 2 | 2016-08-25 |
| 3 | 2014-04-13 |
此结果可以再连接回原 example 表,以确定每个求职者最后工作的公司:
SELECT data.applicant_id, data.company_name, data.max_date
FROM (
SELECT *, CASE WHEN end_date IS NULL THEN 'present' ELSE end_date END AS max_date
FROM example
) data
INNER JOIN (
SELECT
applicant_id,
CASE WHEN MAX(end_date IS NULL) = 1 THEN 'present' ELSE MAX(end_date) END AS max_date
FROM example
GROUP BY applicant_id
) j ON data.applicant_id = j.applicant_id AND data.max_date = j.max_date;
结果:
| applicant_id | company_name | max_date |
|---|---|---|
| 1 | present | |
| 2 | Woodworking.com | 2016-08-25 |
| 3 | NFL.com | 2014-04-13 |