使用包含 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 | Google          | NULL       |
|            1 | Initech         | 2013-01-31 |
|            2 | Woodworking.com | 2016-08-25 |
|            2 | NY Times        | 2013-11-10 |
|            3 | NFL.com         | 2014-04-13 |
+--------------+-----------------+------------+

你的任務是撰寫一個返回 2016-01-01 之後所有行的查詢,包括仍在公司工作的所有員工(具有 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 而不是布林值 TRUEFALSE。因此,具有 NULL end_date 的行既不大於 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 | Google          | 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 WHENNULL 問題提供了一個解決方法:

 SELECT
    applicant_id,
    CASE WHEN MAX(end_date is null) = 1 THEN 'present' ELSE MAX(end_date) END
    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 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 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 | Google          | present    |
|            2 | Woodworking.com | 2016-08-25 |
|            3 | NFL.com         | 2014-04-13 |
+--------------+-----------------+------------+

這些只是在 MySQL 中使用 NULL 值的幾個例子。