PostgreSQLで一覧画面や管理画面を作る際、ORDER BY による並び替えはよく使います。
しかし、並び替え対象のカラムに NULL が含まれている場合、期待と異なる位置に表示されることがあります。
たとえば、期限日や更新日時、スコア、在庫数などでソートしたときに、「値が未設定」のデータが一覧の先頭に来てしまうと、ユーザーにとって見づらい画面になりがちです。
今回はこうした場合に有効な句である、NULLS FIRST ・NULLS LASTについて紹介します。
基本構文
ORDER BY カラム名 [ASC | DESC] [NULLS FIRST | NULLS LAST]
NULLS FIRST はNULLを先頭に、NULLS LAST はNULLを末尾に並べる指定です。
たとえば、在庫管理システムで期限日が設定されていない商品を最後に表示したい場合は、次のように書きます。
SELECT name, expired_at
FROM inventories
ORDER BY expired_at ASC NULLS LAST;
この指定により、期限日が入っている商品が日付の古い順に並び、expired_at が NULL の商品は一覧の最後に表示されます。
PostgreSQLにおけるNULLのデフォルト挙動
PostgreSQLでは、ORDER BY のNULLの扱いは次のようになっています。
ASC、つまり昇順の場合: デフォルトはNULLS LASTDESC、つまり降順の場合: デフォルトはNULLS FIRST
つまり、昇順ではNULLが最後に表示されますが、降順ではNULLが最初に表示されます。
たとえば、更新日時が新しい順に並べたい場合に次のようなSQLを書くとします。
SELECT name, updated_at
FROM users
ORDER BY updated_at DESC;
この場合、updated_at が NULL のユーザーが先頭に表示される可能性があります。
「最近更新されたユーザーを見たい」という画面で、更新日時が未設定のデータが上に出てくると、ユーザーにとっては少し不自然です。
そのような場合は、次のように NULLS LAST を指定します。
SELECT name, updated_at
FROM users
ORDER BY updated_at DESC NULLS LAST;
これにより、更新日時が入っているデータを新しい順に表示し、未更新または未設定のデータは最後にまとめることができます。
UXを考えるならNULLの位置は明示する
SQLとしては NULLS FIRST や NULLS LAST を省略しても動作します。
ただし、一覧画面の見やすさやユーザー体験を考えると、NULLの表示位置は明示しておくのがおすすめです。
特に、以下のようなケースでは NULLS LAST がよく使われます。
- 期限日が近い順に並べたいが、期限未設定のものは最後にしたい
- 更新日時が新しい順に並べたいが、未更新データは最後にしたい
- スコアや金額が高い順に並べたいが、未入力データは最後にしたい
- 優先度順に並べたいが、優先度未設定のものは最後にしたい
一方で、未設定のデータを優先的に確認したい業務画面では、あえて NULLS FIRST を使うこともあります。
SELECT name, priority
FROM tasks
ORDER BY priority ASC NULLS FIRST;
このように、NULLを「後回しにしたい情報」と見るのか、「先に対応すべき未入力情報」と見るのかによって、指定を使い分けるとよいでしょう。
複数カラムでソートする場合
ORDER BY で複数のカラムを指定する場合、NULLS FIRST や NULLS LAST はそれぞれのカラムごとに指定できます。
SELECT name, category, expired_at
FROM inventories
ORDER BY category ASC, expired_at ASC NULLS LAST;
この例では、まず category の昇順で並べ、その中で expired_at の昇順に並べています。
期限日がNULLのデータは、各カテゴリ内で最後に表示されます。
まとめ
PostgreSQLでは、ORDER BY の並び順によってNULLのデフォルト表示位置が変わります。
特に DESC ではNULLが先頭に来るため、画面によっては意図しない見え方になることがあります。
一覧画面や検索結果など、ユーザーがデータを確認する画面では、NULLの位置を明示的に指定しておくと、表示の意図がSQLからも読み取りやすくなります。
ORDER BY カラム名 DESC NULLS LAST;
このように書くだけで、「値があるデータを優先して見せ、未設定のデータは最後に回す」という自然な並び順を実現できます。
小さな指定ですが、管理画面や業務システムの使いやすさを高めるうえで有効なテクニックだな〜と感じました。