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