SQLで「緊急→対応中→未着手→完了」のような独自の順番に並べたいときは、`ORDER BY CASE` で各ステータスを数値の並び順に変換します。NULLや同じ優先度の行にも順序を決めると、画面や帳票の結果がぶれにくくなります。
結論:ORDER BY CASEに独自の順位を返す
文字列の `ORDER BY status` は通常、指定した業務上の優先順位ではなく文字列の照合規則に従って並びます。求める順序を自分で決めるなら、`CASE` で数値を割り当て、その数値を昇順に並べます。
SELECT id, status
FROM tasks
ORDER BY CASE status
WHEN 'urgent' THEN 0
WHEN 'in_progress' THEN 1
WHEN 'todo' THEN 2
WHEN 'done' THEN 3
ELSE 9
END ASC, id ASC;
`ELSE 9` は定義していないステータスを後ろに置くための保険です。`status` がNULLの行も `ELSE` に入ります。`id ASC` は同じステータスの行の順番を安定させるために入れています。
サンプルデータと期待する出力順
以下は `id` が一意で、`status` と `priority` を持つ小さな `tasks` テーブルです。DBに既存の同名テーブルがある環境ではそのまま実行せず、検証用DBで試してください。
CREATE TABLE tasks (
id INTEGER PRIMARY KEY,
status VARCHAR(20),
priority INTEGER
);
INSERT INTO tasks (id, status, priority) VALUES
(11, 'urgent', 5),
(12, 'urgent', NULL),
(13, 'in_progress', 2),
(14, 'todo', 1),
(15, 'done', 3),
(16, NULL, NULL);
| 期待する並び | id | status | priority |
|---|---|---|---|
| 1 | 11 | urgent | 5 |
| 2 | 12 | urgent | NULL |
| 3 | 13 | in_progress | 2 |
| 4 | 14 | todo | 1 |
| 5 | 15 | done | 3 |
| 6 | 16 | NULL | NULL |
「緊急」を最初にし、ステータスが定義されていない行は最後へ。このように期待する行IDの順番を決めてからSQLを書くと、コードレビューで仕様を照合しやすくなります。
優先度とNULLを組み合わせた実用SQL
ステータスが同じとき、`priority` の大きい値を先に出したい場合を考えます。NULL優先度は後ろに回し、最後に一意なidで順序を確定させます。
SELECT id, status, priority
FROM tasks
ORDER BY
CASE status
WHEN 'urgent' THEN 0
WHEN 'in_progress' THEN 1
WHEN 'todo' THEN 2
WHEN 'done' THEN 3
ELSE 9
END ASC,
CASE WHEN priority IS NULL THEN 1 ELSE 0 END ASC,
priority DESC,
id ASC;
ポイントは「ステータス順位 → NULL有無 → 数値優先度 → 一意キー」という順です。後ろの並び替え式は、それまでの式で同順位になった行だけに影響します。
用語解説:NULLS FIRST / NULLS LAST
PostgreSQLなどでNULLを先頭・末尾のどちらへ置くか指定する構文です。対応するDBや既定の順序が異なるため、複数DBで使うSQLではCASEによる明示的なNULL判定も選択肢になります。
よくある間違い:期待順を文字列順に任せる
次の書き方は構文エラーではありませんが、業務の優先度を表していません。新しいステータスを追加したとき、名称の辞書順に依存して想定外の位置へ入ることもあります。
-- これでは独自の業務優先順は保証されない
SELECT id, status
FROM tasks
ORDER BY status ASC;
`CASE` の分岐漏れを防ぐため、最後には必ず `ELSE` を用意することをおすすめします。値の順序が「0→1→2→3」であることも、画面の仕様書と合わせて確認しましょう。
(CASE式そのものの条件分岐が分からない場合については『SQL CASE式の使い方|SELECT・WHERE・UPDATEで条件分岐』をご参照ください)
DBごとに変わりやすいNULLの既定順序
NULLは普通の数値や文字列のように比較できません。`ORDER BY priority DESC` のNULL配置をDBの既定動作だけに任せると、移植先で見え方が変わる可能性があります。
| 要件 | 書き方 | 考え方 |
|---|---|---|
| どのDBでもNULLを最後へ寄せたい | CASE WHEN priority IS NULL THEN 1 ELSE 0 END | 0を先、1を後へ |
| PostgreSQLでNULLを最後へ | priority DESC NULLS LAST | 対応するDBの構文を使う |
| 同順位まで順序固定 | id ASCを最後に追加 | 一意な値で並び順を確定 |
PostgreSQLの公式資料では `ORDER BY` の各式に `ASC` / `DESC` や `NULLS FIRST` / `NULLS LAST` を指定できます。移植性が必要なシステムでは、利用しているDBのドキュメントを確認してください。
WHERE・SELECTのCASEとは何が違うか
`CASE` は条件に応じて値を返す式です。`SELECT` でラベルを作る、`WHERE` で条件式の一部にする、`ORDER BY` で並び替えキーを作る、といった使い分けができます。
今回必要なのは表示順序の制御だけです。`CASE` を `SELECT` にも書いて順位列を返す必要はありません。デバッグ時に順位を可視化したいときだけ、SELECT句へ同じ式を追加すると確認しやすくなります。
(SELECT文とORDER BYの基礎を確認したい場合については『SQLコマンド一覧21選』をご参照ください)
重いクエリで使う場合の注意点
`ORDER BY CASE` は分かりやすい一方、並べ替え用の式が追加されます。大量の結果を処理する場合、既存のインデックスだけで期待どおりに高速化できるとは限りません。
- `WHERE` で対象行を絞れるなら、不要な行を先に減らす。
- `EXPLAIN` などでソート処理と実行計画を確認する。
- 優先度の業務ルールが固定ならマスターテーブル等への分離も検討する。
- 新しいステータスが増える運用では `ELSE` の扱いを仕様化する。
性能が気になるときはSQLの見た目だけで判断せず、実データ量で計測してください。
よくある質問
CASEにないステータスはどこへ並びますか?
このサンプルではELSE 9なので最後のグループに入ります。同グループ内は後続の並び替え式で順番が決まります。
ORDER BYを二つ以上書く必要がありますか?
ORDER BY句はひとつで、その中にカンマ区切りで複数の式を並べます。先に指定した式の順位が優先されます。
statusがNULLのとき `WHEN NULL` と書けば一致しますか?
通常のCASE status WHEN NULLでは一致しません。NULLはIS NULLで判定するか、今回のようにELSEへ含める設計にします。
まず6行程度の検証データで期待するidの並び順を書き出し、SQLの結果と一致するか確認してみてください。