MySQLとPostgreSQLではnullを含んだカラムでソートするときのnullの扱いが違う。 MySQLでnullは、nullでない値よりも小さな値としてソートされ、PostgreSQLでは、nullでない値よりも大きな値としてソートされる。
そのnullの扱いが自分の意図したものと異なる場合や、データベースを変更する場合などに困ったことになることもある。
MySQLとPostreSQLのnullの扱いの違い
例えば、以下のようなデータがあるとする。
> SELECT * FROM counter; +----+-------+ | id | count | +----+-------+ | 1 | NULL | | 2 | 4 | | 3 | 5 | | 4 | 1 | | 5 | 5 | | 6 | NULL | | 7 | 5 | | 8 | 3 | | 9 | 2 | | 10 | 3 | +----+-------+ 10 rows in set (0.00 sec)
id=1とid=6のデータがnullになっている。
これをMySQLでソートすると以下のようになる。
> SELECT * FROM counter ORDER BY count; +----+-------+ | id | count | +----+-------+ | 1 | NULL | | 6 | NULL | | 4 | 1 | | 9 | 2 | | 8 | 3 | | 10 | 3 | | 2 | 4 | | 3 | 5 | | 5 | 5 | | 7 | 5 | +----+-------+ 10 rows in set (0.00 sec)
PostgreSQLでは以下のようになる。
> SELECT * FROM counter ORDER BY count; id | count ----+------- 4 | 1 9 | 2 8 | 3 10 | 3 2 | 4 3 | 5 5 | 5 7 | 5 1 | 6 | (10 rows)
MySQLではnullが上に、PostgreSQLでは下にくる。
PostgreSQLと同じようにソートする方法1[ORDER BY (CASE WHEN ...)]
まず1つ目の方法は、CASE WHENを使う方法だ。 これを使ってcountがnullだった場合は2147483647(4バイトint最大値)として扱ってソートする。
> SELECT * FROM counter ORDER BY (CASE WHEN count IS NULL THEN 2147483647 ELSE count END); +----+-------+ | id | count | +----+-------+ | 4 | 1 | | 9 | 2 | | 10 | 3 | | 8 | 3 | | 2 | 4 | | 3 | 5 | | 5 | 5 | | 7 | 5 | | 6 | NULL | | 1 | NULL | +----+-------+ 10 rows in set (0.01 sec)
以下のように、(CASE WHEN ...)でソートした後に、countでソートする方法もある。
> SELECT * FROM counter ORDER BY (CASE WHEN count IS NULL THEN 1 ELSE 0 END), count; +----+-------+ | id | count | +----+-------+ | 4 | 1 | | 9 | 2 | | 10 | 3 | | 8 | 3 | | 2 | 4 | | 3 | 5 | | 5 | 5 | | 7 | 5 | | 6 | NULL | | 1 | NULL | +----+-------+ 10 rows in set (0.00 sec)
ただ、この方法を使うのなら後述する方法2の方が簡単に書ける。
PostgreSQLと同じようにソートする方法2[ORDER BY column IS NOT NULL, column]
以下のようにすれば、CASE WHENを使うよりも簡単に書くことができる。
> SELECT * FROM counter ORDER BY count IS NULL, count; +----+-------+ | id | count | +----+-------+ | 4 | 1 | | 9 | 2 | | 10 | 3 | | 8 | 3 | | 2 | 4 | | 3 | 5 | | 5 | 5 | | 7 | 5 | | 6 | NULL | | 1 | NULL | +----+-------+ 10 rows in set (0.00 sec)