LoginSignup
2
3

More than 5 years have passed since last update.

本当は怖いNULLの世界(データベース)

Last updated at Posted at 2019-01-12

はじめに

自分を戒めるためにNULLが入っているテーブルに対して、関数を実行するとどういう結果になるのか調べてみた。

なお、SQLはMySQLを使用しています。PostgreSQLやOracle、SQL Serverなどでは挙動が変わる可能性があります。

算術

NULLを使った算術は全てNULLになる。

SELECT NULL + 10, NULL - 10, 10 * NULL, 10 / NULL;

結果

NULL + 10 NULL - 10 10 * NULL 10 / NULL
NULL NULL NULL NULL

絞り込み関数

絞り込み条件でよく使う関数を見ていきたいと思います。

IN

次のようなテーブルを作る。

-- 有名人テーブル
CREATE TABLE celebrities
(id INT PRIMARY KEY AUTO_INCREMENT, 
 name VARCHAR(255), 
 age INT);

-- 社員テーブル
CREATE TABLE personnel
(id int PRIMARY KEY AUTO_INCREMENT, 
 name varchar(255), 
 age int);

有名人(celebrities)

id name age
1 松本人志 55
2 明石家さんま 63
3 安室奈美恵 NULL

社員(personnel)

id name age
1 山田太郎 24
2 佐藤一 55
3 藤原正 60

社員の中で有名人と年齢が同じ人を探したい。

SELECT *
  FROM personnel
 WHERE age IN (
  SELECT age
    FROM celebrities
);

結果

id name age
2 佐藤一 55

有名人の年齢にNULLが入っているので、空集合が返ってくると思っていたが、一行返ってきた。

では、社員の年齢にNULLが入っていたらどうなるのか。

id name age
1 山田太郎 24
2 佐藤一 55
3 藤原正 60
4 近藤久義 NULL

同じクエリを実行する

結果

id name age
2 佐藤一 55

同じ結果になった。NULLは同じと判定されない。

NOT IN

SELECT *
  FROM personnel
 WHERE age NOT IN (
  SELECT age
    FROM celebrities
);

結果

id name age

一行も返ってこない。

EXISTS

上記と同じテーブルを用意。

celebrities

id name age
1 松本人志 55
2 明石家さんま 63
3 安室奈美恵 NULL

personnel

id name age
1 山田太郎 24
2 佐藤一 55
3 藤原正 60
4 近藤久義 NULL

EXISTSを使って、有名人と年齢が同じ社員を抽出

 SELECT *
   FROM personnel p
  WHERE EXISTS (
    SELECT *
      FROM celebrities c
     WHERE p.age = c.age
  );

結果

id name age
2 佐藤一 55

INと同じ

NOT EXISTS

問題はNOT EXISTS

celebrities

id name age
1 松本人志 55
2 明石家さんま 63
3 安室奈美恵 NULL

personnel

id name age
1 山田太郎 24
2 佐藤一 55
3 藤原正 60
4 近藤久義 NULL
 SELECT *
   FROM personnel p
  WHERE NOT EXISTS (
    SELECT *
      FROM celebrities c
     WHERE p.age = c.age
  );

結果

id name age
1 山田太郎 24
3 藤原正 60
4 近藤久義 NULL

NOT INとは結果が違う。

LIKE

次のテーブルを使用

celebrities

id name age
1 松本人志 55
2 明石家さんま 63
3 NULL 33
SELECT *
  FROM celebrities
 WHERE name LIKE '%'

結果

id name age
1 松本人志 55
2 明石家さんま 63

名前がNULLは抽出されない。

整列

ORDER BY

celebrities

id name age
1 松本人志 55
2 明石家さんま 63
3 安室奈美恵 NULL
SELECT *
  FROM celebrities
 ORDER BY age;

結果

id name age
3 安室奈美恵 NULL
1 松本人志 55
2 明石家さんま 63

NULLが一番最初になる。
DESC(降順)は逆になる。

集約関数

GROUP BY, COUNT

以下のテーブルを用意
students

CREATE TABLE students
(id INT PRIMARY KEY AUTO_INCREMENT, 
 name VARCHAR(255), 
 class VARCHAR(255));

students

id name class
1 小田切竜 A
2 神谷俊輔 A
3 内山春彦 B
4 沢田慎 B
5 熊井輝夫 NULL
6 毛利研一 NULL

クラスの人数を抽出

SELECT class, COUNT(*)
  FROM students
 GROUP BY class;

結果

id COUNT(*)
NULL 2
A 2
B 2

NULLは一つに集約される。(本来はNULL同士は同じ値ではない。)

SUM

CREATE TABLE scores
(id INT, 
 subject VARCHAR(255), 
 score INT,
 PRIMARY KEY (id, subject));

students(前のを流用)

id name class
1 小田切竜 A
2 神谷俊輔 A
3 内山春彦 B
4 沢田慎 B

scores

id subject score
1 国語 60
1 英語 75
2 国語 77
2 英語 53
3 国語 NULL
3 英語 60
4 国語 90
4 英語 100

休みで国語のテストが受けられなかったのでNULLになってしまった。
生徒の合計得点を取得する

 -- MySQLではGROUP BYに入っていないカラムでもそのままSELECTで抽出できるが、一応MAXで他ベンダーにも対応
 SELECT MAX(st.name), SUM(score)
   FROM students st
  INNER JOIN scores sc
     ON st.id = sc.id
  GROUP BY st.id;

結果

MAX(st.name) SUM(score)
小田切竜 135
神谷俊輔 130
内山春彦 60
沢田慎 190

NULLは0として合計されるらしい。
NULLの演算は必ずNULLになる(SELECT NULL + 10; => NULL)ので、NULLになるかと思った。

MIN, MAX, AVG

students

id name class
1 小田切竜 A
2 神谷俊輔 A
3 内山春彦 B
4 沢田慎 B

scores

id subject score
1 国語 60
1 英語 75
2 国語 77
2 英語 53
3 国語 NULL
3 英語 60
4 国語 90
4 英語 100

各教科の最低点と最高点と平均点を求める

 SELECT subject AS '教科', 
        MIN(score) AS '最低得点',
        MAX(score) AS '最高得点',
        AVG(score) AS '平均点'
   FROM students st
  INNER JOIN scores sc
     ON sc.id = st.id
  GROUP BY subject;

結果

教科 最低得点 最高得点 平均点
国語 60 90 75.6667
英語 53 100 72.000

NULL以外の最小値と最大値と平均が出てる。

国語はNULLがいるので3人の平均点が出ている。英語は4人の平均点が出ている。

集合演算

UNION, UNION ALL

a

value
a
a
b
c
NULL

b

value
b
b
c
d
NULL
NULL
SELECT value
  FROM a
 UNION
SELECT value
  FROM b;

結果

value
a
b
c
NULL
d

UNIONは重複行を排除する。NULLもまとめられてる。

SELECT value
  FROM a
 UNION ALL
SELECT value
  FROM b;

結果

value
a
a
b
c
NULL
b
b
c
d
NULL
NULL

UNION ALLは全部つなげる。NULLもそのまま。

まとめ

NULLは薬だと思ってほしい。正しく使っている限りは有用だが、乱用すればすべてをぶち壊す。最良の選択は、可能な限り使用を避けて、どうしても使わざるを得ないときだけ、適切に使用することだ。
ジョー・セルコ著 ミック監訳 『プログラマのためのSQL 第4版』 p 261

NULLの用法・容量は正しく。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3