UNIONで結果をまとめたあとにORDER BYを付けると、「どこに書くのか」「どの列名を使うのか」でつまずきやすくなります。まず覚えるのは、最終結果を並び替えたいならORDER BYは集合演算の最後に置く、という形です。
まず結論:最終結果を並び替えるORDER BYはUNIONの最後
SELECT id, name FROM users_a
UNION ALL
SELECT id, name FROM users_b
ORDER BY name;
このORDER BYは2つ目のSELECTだけではなく、UNION ALLで結合された最終結果に適用されます。UNIONでも考え方は同じです。
(UNIONとUNION ALLの基本から確認したい場合については『SQL UNIONの使い方をやさしく解説|違い・注意点・活用例まとめ』をご参照ください)
原因1:途中のSELECTにORDER BYを書いている
各SELECTの途中へそのままORDER BYを書くと、DBによって構文エラーになったり、意図した意味にならなかったりします。最終結果を並べたいだけなら最後へ1回書きます。
-- 最終結果を並べる
SELECT id, created_at FROM logs_a
UNION ALL
SELECT id, created_at FROM logs_b
ORDER BY created_at DESC;
原因2:ORDER BYで使う列名が出力列と合っていない
UNION後のORDER BYでは、集合結果として見える列名やaliasを使います。DB差を避ける意味でも、先頭SELECTで分かりやすいaliasを付けると安全です。
SELECT id, created_at AS sort_time FROM logs_a
UNION ALL
SELECT id, updated_at FROM logs_b
ORDER BY sort_time DESC;
2つ目のSELECTで別の列名を使っていても、最終結果の列構造として扱われます。実際にどの名前で参照できるかは利用DBの仕様を確認してください。
原因3:各SELECTで先に並べたい意図と、最終結果の並び替えを混同している
個別SELECTにLIMITやORDER BYを適用してからUNIONしたい場合は、括弧でquery blockを分ける必要があります。これは最終結果を並び替えるケースとは別です。
(SELECT id, created_at FROM logs_a ORDER BY created_at DESC LIMIT 10)
UNION ALL
(SELECT id, created_at FROM logs_b ORDER BY created_at DESC LIMIT 10)
ORDER BY created_at DESC;
PostgreSQLやMySQLの公式ドキュメントでも、括弧の有無によってLIMITやORDER BYが個別queryへ付くのか、集合演算後の結果へ付くのかが変わる点が説明されています。
UNIONとUNION ALLでORDER BYの書き方は変わる?
最終結果を並び替える基本形は同じです。違いは重複行の扱いです。UNIONは重複を除去し、UNION ALLは重複を残します。順序自体はORDER BYで明示しない限り保証されません。
| 構文 | 重複 | 最終並び順 |
|---|---|---|
| UNION | 除去する | ORDER BYで指定 |
| UNION ALL | 残す | ORDER BYで指定 |
エラーや想定外の順序になったときの確認順
- ORDER BYがUNION全体の末尾にあるか
- ORDER BYの列名またはaliasが最終出力列として使えるか
- 個別SELECTへORDER BY/LIMITを付けるなら括弧が必要か
- UNIONとUNION ALLを取り違えていないか
- 利用しているDB製品の構文差を公式ドキュメントで確認したか
特に「各SELECTを先に並べたい」のか「結合した最終結果を並べたい」のかを分けると、原因を切り分けやすくなります。
まとめ:最終結果のORDER BYから考える
- UNION全体を並べるORDER BYは最後に置く
- ORDER BYでは最終出力列として参照できる名前を使う
- 個別queryへORDER BY/LIMITを効かせる場合は括弧を検討する
- UNION/UNION ALLだけでは行順は保証されない
まず手元のSQLでORDER BYを集合演算の最後へ移し、次に列aliasと括弧の有無を確認してみてください。