SQLでデータを検索した結果が大量に返ってきたとき、順序がバラバラだと読みにくく・使いにくいですよね。
そんなときに活躍するのがORDER BY句です。好きな項目で並び替えて、一覧性や利便性をグッと高めてくれます。
ソートが役立つ場面
- 売上データを金額の高い順に見たい
- 社員リストを名前順に整理したい
- 商品在庫を少ない順に確認したい
- 成績表を点数順に並べたい
基本構文:ORDER BYの書き方

基本の形
SELECT 列名1, 列名2, ...
FROM テーブル名
ORDER BY 並び替えたい列 [ASC | DESC];
重要なポイント:
ASC:昇順(小さい順、あ→ん、A→Z)※省略可能DESC:降順(大きい順、ん→あ、Z→A)
例1:年齢順に並べる(昇順)
-- 年齢の若い人から順に表示
SELECT name, age
FROM users
ORDER BY age ASC;
実行結果例:
name | age
--------|----
田中 | 22
佐藤 | 25
山田 | 30
鈴木 | 35
ASCは省略可能:
-- これも同じ結果
SELECT name, age
FROM users
ORDER BY age;
例2:年齢順に並べる(降順)
-- 年齢の高い人から順に表示
SELECT name, age
FROM users
ORDER BY age DESC;
実行結果例:
name | age
--------|----
鈴木 | 35
山田 | 30
佐藤 | 25
田中 | 22
例3:文字列データの並び替え
-- 名前をあいうえお順に並べる
SELECT name, department
FROM employees
ORDER BY name ASC;
実行結果例:
name | department
---------|----------
青木 | 営業部
石田 | 開発部
上田 | 営業部
大野 | 総務部
複数条件で並び替える

基本的な考え方
複数の条件を指定すると、最初の条件で並び替えて、同じ値の場合は次の条件で並び替えます。
例1:部署 → 年齢の順に並べる
SELECT name, department, age
FROM employees
ORDER BY department ASC, age DESC;
実行結果例:
name | department | age
---------|------------|----
山田 | 営業部 | 35
佐藤 | 営業部 | 28
田中 | 営業部 | 25
鈴木 | 開発部 | 32
青木 | 開発部 | 26
解説:
- まず部署名で昇順(あいうえお順)に並び替え
- 同じ部署内では年齢で降順(高い順)に並び替え
例2:売上データの並び替え
-- 年月 → 売上金額の順に並べる
SELECT sales_date, product_name, amount
FROM sales
ORDER BY sales_date DESC, amount DESC;
使い方のコツ:
- 最初の条件が最も優先される
- 条件は
,(カンマ)で区切る - 各条件に個別にASC/DESCを指定できる
例3:3つ以上の条件
-- 地域 → 部署 → 年齢の順
SELECT name, region, department, age
FROM employees
ORDER BY region ASC, department ASC, age DESC;
列番号で指定する方法
基本的な使い方
-- SELECTの2番目の列(age)で降順ソート
SELECT name, age
FROM users
ORDER BY 2 DESC;
列番号の対応:
SELECT name, age, department -- 1番目, 2番目, 3番目
FROM employees
ORDER BY 3, 2 DESC; -- 部署順、その中で年齢降順
注意点
推奨しない理由:
- 列の順序を変更するとソート順も変わってしまう
- 何の列でソートしているかわかりにくい
- 保守性が低い
良い例と悪い例:
-- 悪い例
ORDER BY 2 DESC
-- 良い例
ORDER BY age DESC
列番号が有効な場面
- 一時的なデータ確認で手早く並び替えたいとき
- SELECT文が非常に長い場合の簡略表記
NULLの並び順を制御する
NULLとは?
NULLは「値が存在しない」「未入力」を意味するデータです。
データベース別のNULL処理
| データベース | NULLの扱い(ASC) | NULLの扱い(DESC) |
|---|---|---|
| MySQL | NULLが最初 | NULLが最後 |
| PostgreSQL | NULLが最後 | NULLが最初 |
| Oracle | NULLが最後 | NULLが最初 |
| SQL Server | NULLが最初 | NULLが最後 |
NULL位置の明示的な制御
PostgreSQL、Oracleの場合
-- NULLを最後に配置
SELECT name, age
FROM users
ORDER BY age ASC NULLS LAST;
-- NULLを最初に配置
SELECT name, age
FROM users
ORDER BY age DESC NULLS FIRST;
MySQLの場合
-- NULLを最後に配置
SELECT name, age
FROM users
ORDER BY age IS NULL, age ASC;
-- NULLを最初に配置
SELECT name, age
FROM users
ORDER BY age IS NULL DESC, age ASC;
実用例
例1:連絡先リストの整理
-- 電話番号が登録済みの人を先に、未登録を後に
SELECT name, phone
FROM customers
ORDER BY phone IS NULL, name ASC;
実行結果例:
name | phone
---------|-------------
田中 | 090-1234-5678
山田 | 080-9876-5432
佐藤 | NULL
鈴木 | NULL
例2:商品の価格順リスト
-- 価格が設定されている商品を先に表示
SELECT product_name, price
FROM products
ORDER BY price IS NULL, price ASC;
応用:他のSQL句との組み合わせ

LIMITと組み合わせて上位N件を取得
例1:成績上位者の取得
-- 点数が高い順に上位5名を取得
SELECT name, score
FROM students
ORDER BY score DESC
LIMIT 5;
実行結果例:
name | score
---------|------
山田 | 95
田中 | 92
佐藤 | 88
鈴木 | 85
青木 | 82
例2:最新の投稿を取得
-- 最新の投稿10件を取得
SELECT title, created_at
FROM posts
ORDER BY created_at DESC
LIMIT 10;
例3:売上トップ商品の取得
-- 今月の売上トップ3商品
SELECT product_name, SUM(amount) as total_sales
FROM sales
WHERE MONTH(sales_date) = MONTH(CURRENT_DATE)
GROUP BY product_name
ORDER BY total_sales DESC
LIMIT 3;
WHEREと組み合わせた絞り込み
-- 営業部の社員を年齢順に表示
SELECT name, age, department
FROM employees
WHERE department = '営業部'
ORDER BY age ASC;
GROUP BYと組み合わせた集計結果の並び替え
-- 部署別の平均年齢を高い順に表示
SELECT department, AVG(age) as avg_age
FROM employees
GROUP BY department
ORDER BY avg_age DESC;
SQL文の正しい順序
基本的な順序
SELECT 列名
FROM テーブル名
WHERE 条件
GROUP BY グループ化する列
HAVING グループ化後の条件
ORDER BY 並び替える列
LIMIT 取得件数;
順序を間違えた場合のエラー
-- ❌ 間違い:ORDER BYがWHEREより前
SELECT name, age
FROM users
ORDER BY age DESC
WHERE age >= 20; -- エラーになる
-- ✅ 正しい:WHEREの後にORDER BY
SELECT name, age
FROM users
WHERE age >= 20
ORDER BY age DESC;
実用例:複雑なクエリ
-- 2023年の売上データから、部署別売上を計算し、
-- 1000万円以上の部署を売上順に表示
SELECT
department,
SUM(amount) as total_sales
FROM sales s
JOIN employees e ON s.employee_id = e.id
WHERE YEAR(s.sales_date) = 2023
GROUP BY department
HAVING SUM(amount) >= 10000000
ORDER BY total_sales DESC;
実務でよく使うパターン
パターン1:日付データの並び替え
最新順(新しい日付から)
-- 最新の注文から表示
SELECT order_id, customer_name, order_date
FROM orders
ORDER BY order_date DESC;
古い順(過去の日付から)
-- 古い注文から表示
SELECT order_id, customer_name, order_date
FROM orders
ORDER BY order_date ASC;
月別での並び替え
-- 月別売上を月順に表示
SELECT
MONTH(order_date) as month,
SUM(amount) as monthly_sales
FROM orders
WHERE YEAR(order_date) = 2023
GROUP BY MONTH(order_date)
ORDER BY month ASC;
パターン2:文字列データの並び替え
五十音順(あいうえお順)
-- 顧客名を五十音順で表示
SELECT customer_name, phone
FROM customers
ORDER BY customer_name ASC;
アルファベット順
-- 商品コードを昇順で表示
SELECT product_code, product_name
FROM products
ORDER BY product_code ASC;
パターン3:数値データの並び替え
売上ランキング
-- 社員別売上ランキング
SELECT
employee_name,
SUM(amount) as total_sales
FROM sales
GROUP BY employee_name
ORDER BY total_sales DESC;
在庫少ない順
-- 在庫が少ない商品から表示(発注優先度)
SELECT product_name, stock_quantity
FROM products
WHERE stock_quantity > 0
ORDER BY stock_quantity ASC;
パターン4:複合条件での実用例
顧客管理システム
-- 地域別、契約金額順の顧客リスト
SELECT
customer_name,
region,
contract_amount,
contract_date
FROM customers
WHERE contract_amount > 0
ORDER BY region ASC, contract_amount DESC, contract_date DESC;
従業員管理システム
-- 部署別、役職別、入社年数順の従業員リスト
SELECT
name,
department,
position,
hire_date,
DATEDIFF(CURRENT_DATE, hire_date) / 365 as years_of_service
FROM employees
ORDER BY
department ASC,
position DESC,
hire_date ASC;
パフォーマンスを考慮したソート

インデックスの活用
インデックスが効く場合
-- age列にインデックスがある場合、高速
SELECT name, age
FROM users
ORDER BY age;
インデックスが効かない場合
-- 計算結果でのソートは遅い
SELECT name, age
FROM users
ORDER BY age * 2 + 10;
大量データでの注意点
LIMIT を使った効率化
-- 悪い例:全データを並び替えてから上位10件
SELECT name, score
FROM students
ORDER BY score DESC; -- 全データソート後、アプリで10件取得
-- 良い例:データベース内で上位10件に絞る
SELECT name, score
FROM students
ORDER BY score DESC
LIMIT 10; -- データベース内で10件に絞る
複合インデックスの活用
-- department, age の複合インデックスがある場合
SELECT name, department, age
FROM employees
ORDER BY department, age; -- 高速
エラーが起きやすいパターンと対処法
エラー1:列名の間違い
-- ❌ 存在しない列でソート
SELECT name, age
FROM users
ORDER BY ages; -- 'ages'は存在しない
-- ✅ 正しい列名を使用
SELECT name, age
FROM users
ORDER BY age;
エラー2:GROUP BYとの組み合わせミス
-- ❌ GROUP BYにない列でソート
SELECT department, COUNT(*)
FROM employees
GROUP BY department
ORDER BY name; -- nameはGROUP BYにない
-- ✅ GROUP BYに含まれる列か集計関数を使用
SELECT department, COUNT(*) as emp_count
FROM employees
GROUP BY department
ORDER BY emp_count DESC;
エラー3:データ型の不一致
-- ❌ 文字列として保存された数値のソート
-- '1', '10', '2' → '1', '10', '2' (文字列順)
-- ✅ 数値として変換してソート
SELECT product_code
FROM products
ORDER BY CAST(product_code AS INTEGER);
データベース固有の機能
MySQL固有の機能
FIELD関数による任意順序
-- 指定した順序で並び替え
SELECT name, status
FROM tasks
ORDER BY FIELD(status, '緊急', '高', '中', '低');
日本語の並び替え
-- 日本語を正しく並び替え
SELECT name
FROM customers
ORDER BY name COLLATE utf8mb4_ja_0900_as_cs;
PostgreSQL固有の機能
CASE文での条件付きソート
-- ステータス別の優先順位でソート
SELECT task_name, status, priority
FROM tasks
ORDER BY
CASE status
WHEN '緊急' THEN 1
WHEN '高' THEN 2
WHEN '中' THEN 3
WHEN '低' THEN 4
ELSE 5
END,
priority DESC;
Oracle固有の機能
日本語の読み順ソート
-- ひらがな読み順で並び替え
SELECT name
FROM customers
ORDER BY NLSSORT(name, 'NLS_SORT=JAPANESE_M');
まとめ:ORDER BYの使い分け
| 並び替えたい内容 | SQL構文例 | 使用場面 |
|---|---|---|
| 昇順 | ORDER BY 列名 ASC | 古い順、小さい順、あいうえお順 |
| 降順 | ORDER BY 列名 DESC | 新しい順、大きい順、ランキング |
| 複数条件 | ORDER BY 列1 ASC, 列2 DESC | カテゴリ別にまとめて詳細ソート |
| NULL制御 | ORDER BY 列名 ASC NULLS LAST | データ品質を考慮した表示 |
| 上位取得 | ORDER BY 列名 DESC LIMIT N | ランキング、ベスト○○ |


コメント