「まず部署順、その中で年齢の高い順に並べたい」
「商品をカテゴリごとに分けて、価格の安い順に表示したい」
「同じ点数の学生を学籍番号順で並べたい」
データベースからデータを取得するとき、こうした複数の条件で並び替えをしたいことがよくあります。そんなときに使うのが、ORDER BY句で複数の列を指定する方法です。
この記事では、SQLで複数の列を使って並び替えを行う方法を、データベースが初心者の人でもわかるように、実例を交えて詳しく解説していきます。
ORDER BY の基本をおさらい

単一列での並び替え
まずは、基本的なORDER BYの使い方を確認しましょう。
SELECT 列名1, 列名2
FROM テーブル名
ORDER BY 列名1;
説明: この基本形では、指定した一つの列の値に基づいて、データを並び替えます。
例: 社員テーブルから年齢順にデータを取得する場合
SELECT name, age
FROM employees
ORDER BY age;
昇順と降順の指定
データの並び順は、昇順(小さい順)と降順(大きい順)を指定できます。
-- 昇順(デフォルト)
ORDER BY age ASC;
-- 降順
ORDER BY age DESC;
説明: ASCは昇順(小さい値から大きい値へ)、DESCは降順(大きい値から小さい値へ)を表します。
例: 年齢の高い順(降順)で並び替える場合
SELECT name, age
FROM employees
ORDER BY age DESC;
複数列でのORDER BY基本構文

基本的な書き方
複数の列で並び替えをする場合は、列名をカンマで区切って指定します。
SELECT 列名1, 列名2, 列名3
FROM テーブル名
ORDER BY 列名1, 列名2, 列名3;
重要なポイント: 左から順番に優先順位が決まります。最初に指定した列が最優先、次に指定した列が第2優先となります。
各列に個別の並び順を指定
それぞれの列に対して、昇順か降順かを個別に指定できます。
ORDER BY 列名1 ASC, 列名2 DESC, 列名3 ASC;
説明: この例では、列名1は昇順、列名2は降順、列名3は昇順で並び替えます。
実践的な使用例
例1: 社員を部署別、年齢順で並び替え
以下のような社員テーブルがあるとします:
employees テーブル
name | department | age | salary
--------|------------|-----|-------
山田 | 営業 | 30 | 400000
田中 | 営業 | 25 | 350000
鈴木 | 開発 | 35 | 500000
佐藤 | 開発 | 28 | 450000
高橋 | 営業 | 32 | 420000
目的: まず部署別に分けて、その中で年齢の高い順に表示したい
SELECT name, department, age, salary
FROM employees
ORDER BY department ASC, age DESC;
説明: 最初に部署(department)で昇順に並び替え、同じ部署内では年齢(age)で降順に並び替えます。
例: 実行結果は以下のようになります
name | department | age | salary
--------|------------|-----|-------
鈴木 | 開発 | 35 | 500000
佐藤 | 開発 | 28 | 450000
高橋 | 営業 | 32 | 420000
山田 | 営業 | 30 | 400000
田中 | 営業 | 25 | 350000
例2: 商品をカテゴリ別、価格順で並び替え
SELECT product_name, category, price
FROM products
ORDER BY category ASC, price ASC;
説明: カテゴリごとにグループ分けして、その中で価格の安い順に表示します。
例: 実行結果のイメージ
product_name | category | price
-------------|----------|------
ノート | 文具 | 100
ペン | 文具 | 150
りんご | 食品 | 200
みかん | 食品 | 180
実際の並び順は:
product_name | category | price
-------------|----------|------
ペン | 文具 | 150
ノート | 文具 | 100
みかん | 食品 | 180
りんご | 食品 | 200
例3: 試験結果を点数、学籍番号順で表示
SELECT student_name, student_id, score
FROM exam_results
ORDER BY score DESC, student_id ASC;
説明: まず点数の高い順に並び替え、同じ点数の場合は学籍番号の若い順で並び替えます。
例: 同じ点数の学生がいる場合の並び順
-- 実行前(元の順序)
student_name | student_id | score
-------------|------------|------
田中 | S001 | 85
山田 | S003 | 90
佐藤 | S002 | 85
鈴木 | S001 | 90
-- 実行後(並び替え後)
student_name | student_id | score
-------------|------------|------
山田 | S003 | 90 -- 高得点順
鈴木 | S001 | 90 -- 同点なら学籍番号順
田中 | S001 | 85
佐藤 | S002 | 85
列番号での指定方法

列番号を使った指定
SELECT句の列の順番を番号で指定することもできます。
SELECT name, department, age
FROM employees
ORDER BY 2, 3 DESC;
説明: この例では、SELECT句の2番目の列(department)と3番目の列(age)で並び替えます。
例: 上記は以下と同じ意味になります
ORDER BY department, age DESC;
列番号指定の注意点
メリット:
- SELECT句が長い場合に短く書ける
- 複雑な計算式の列を指定しやすい
デメリット:
- どの列で並び替えているかわかりにくい
- SELECT句の順番を変えると動作が変わってしまう
- メンテナンス性が悪い
推奨: 可読性と保守性の観点から、列名での指定がおすすめです。
計算式やファンクションでの並び替え
計算結果での並び替え
列の値を計算した結果で並び替えることもできます。
SELECT name, price, quantity, price * quantity as total
FROM sales
ORDER BY price * quantity DESC;
説明: 価格と数量をかけた合計金額の大きい順で並び替えます。
文字列関数での並び替え
SELECT name, email
FROM users
ORDER BY LENGTH(name), name;
説明: まず名前の文字数の短い順、同じ文字数なら名前のアルファベット順で並び替えます。
日付関数での並び替え
SELECT name, birth_date
FROM employees
ORDER BY YEAR(birth_date) DESC, MONTH(birth_date), DAY(birth_date);
説明: 生年月日を年→月→日の順番で並び替えます。
NULL値の扱い

NULL値の基本的な動作
並び替えでNULL値がある場合、データベースによって扱いが異なります。
MySQL、SQL Server: NULL値は最初に表示される Oracle、PostgreSQL: NULL値は最後に表示される
NULL値の表示位置を制御
PostgreSQLやOracleでは、NULL値の表示位置を明示的に指定できます。
-- NULLを最後に表示
ORDER BY age ASC NULLS LAST;
-- NULLを最初に表示
ORDER BY age DESC NULLS FIRST;
説明: NULLS LASTはNULL値を最後に、NULLS FIRSTはNULL値を最初に表示します。
例: 年齢にNULL値がある場合の並び替え
SELECT name, age
FROM employees
ORDER BY age ASC NULLS LAST;
MySQL/SQL ServerでのNULL制御
MySQL や SQL Server では、CASE文を使ってNULL値の扱いを制御できます。
-- NULLを最後に表示(MySQL)
ORDER BY CASE WHEN age IS NULL THEN 1 ELSE 0 END, age;
-- NULLを最初に表示(MySQL)
ORDER BY CASE WHEN age IS NULL THEN 0 ELSE 1 END, age;
実践的な応用例

例1: ECサイトの商品一覧
要件: 商品を「在庫あり→在庫なし」「カテゴリ別」「価格の安い順」で表示
SELECT
product_name,
category,
price,
stock,
CASE WHEN stock > 0 THEN '在庫あり' ELSE '在庫なし' END as stock_status
FROM products
ORDER BY
CASE WHEN stock > 0 THEN 0 ELSE 1 END, -- 在庫ありを優先
category, -- カテゴリ別
price ASC; -- 価格の安い順
説明: CASE文を使って在庫のある商品を優先的に表示し、その中でカテゴリ別、価格順に並び替えます。
例2: 社員の評価ランキング
要件: 社員を「部署別」「評価点の高い順」「入社年数の長い順」で表示
SELECT
name,
department,
evaluation_score,
hire_date,
YEAR(CURDATE()) - YEAR(hire_date) as years_of_service
FROM employees
ORDER BY
department, -- 部署別
evaluation_score DESC, -- 評価の高い順
hire_date ASC; -- 入社の早い順
例3: 売上レポートの作成
要件: 売上データを「年→月→売上高の大きい順」で表示
SELECT
YEAR(sale_date) as year,
MONTH(sale_date) as month,
product_name,
SUM(amount) as total_sales
FROM sales
GROUP BY YEAR(sale_date), MONTH(sale_date), product_name
ORDER BY
year DESC, -- 新しい年から
month DESC, -- 新しい月から
total_sales DESC; -- 売上の大きい順
パフォーマンスの考慮事項
インデックスの活用
ORDER BY句で使用する列にインデックスが設定されていると、並び替えが高速になります。
-- 複合インデックスの作成例
CREATE INDEX idx_dept_age ON employees(department, age);
説明: ORDER BY department, age を頻繁に使用する場合、この複合インデックスが効果的です。
インデックスが効果的な順序
インデックスを最大限活用するには、ORDER BY句の列順序とインデックスの列順序を合わせることが重要です。
効果的な例:
-- インデックス: (department, age)
ORDER BY department, age; -- インデックスが使用される
非効率な例:
-- インデックス: (department, age)
ORDER BY age, department; -- インデックスが使用されない可能性
大量データでの注意点
大量のデータを並び替える場合は、以下の点に注意が必要です:
LIMIT句との組み合わせ:
SELECT name, department, age
FROM employees
ORDER BY department, age DESC
LIMIT 10;
説明: 上位10件だけが必要な場合、LIMIT句を使うことで処理を効率化できます。
データベース別の特徴

MySQL での特徴
-- MySQLでのNULL制御
ORDER BY ISNULL(column), column;
-- 文字列の大文字小文字を区別しない並び替え
ORDER BY column COLLATE utf8_general_ci;
PostgreSQL での特徴
-- PostgreSQLでのNULL制御
ORDER BY column NULLS LAST;
-- 配列の要素での並び替え
ORDER BY array_column[1];
SQL Server での特徴
-- SQL ServerでのNULL制御
ORDER BY CASE WHEN column IS NULL THEN 1 ELSE 0 END, column;
-- TOP句との組み合わせ
SELECT TOP 10 * FROM table ORDER BY column1, column2;
Oracle での特徴
-- OracleでのNULL制御
ORDER BY column NULLS FIRST;
-- ROWNUMとの組み合わせ
SELECT * FROM (
SELECT * FROM table ORDER BY column1, column2
) WHERE ROWNUM <= 10;
よくある間違いとその対処法
間違い1: 優先順位の誤解
間違った理解:
ORDER BY age DESC, department ASC;
「年齢の高い人の中で部署順に並ぶ」と思ってしまう
正しい理解: まず年齢の高い順に全体を並べ、同じ年齢の人がいた場合のみ部署順に並ぶ
間違い2: ASC/DESCの省略
-- 意図した動作
ORDER BY department ASC, age DESC;
-- 実際に書いたSQL(間違い)
ORDER BY department, age; -- ageもASC(昇順)になってしまう
対処法: 明示的にASC/DESCを指定する習慣をつける
間違い3: NULL値の考慮不足
-- NULL値がある場合の予期しない結果
ORDER BY hire_date; -- NULL値の位置がDBによって異なる
対処法: NULL値の扱いを明示的に指定する
応用テクニック

カスタム並び順の実現
特定の順序で並び替えたい場合、CASE文を活用します。
SELECT name, priority
FROM tasks
ORDER BY
CASE priority
WHEN '緊急' THEN 1
WHEN '高' THEN 2
WHEN '中' THEN 3
WHEN '低' THEN 4
ELSE 5
END;
説明: 優先度を「緊急→高→中→低」の順序で並び替えます。
複雑な条件での並び替え
SELECT
student_name,
math_score,
english_score,
(math_score + english_score) as total_score
FROM exam_results
ORDER BY
total_score DESC, -- 合計点の高い順
GREATEST(math_score, english_score) DESC, -- 高い方の科目の点数順
student_name; -- 同点なら名前順
まとめ
SQLのORDER BY句で複数列を指定することで、データを思い通りの順序で表示できます。
基本的なポイント
- 複数の列はカンマで区切って指定
- 左から右へ優先順位が決まる
- 各列に個別にASC/DESCを指定可能
- NULL値の扱いはデータベースによって異なる
実践的な活用
- 業務要件に合わせた柔軟な並び替え
- インデックスを活用したパフォーマンス最適化
- CASE文を使ったカスタム並び順の実現
注意事項
- 列名指定が列番号指定より推奨
- 大量データではLIMIT句との組み合わせを検討
- NULL値の扱いを明示的に指定
並び替えパターン別まとめ表
用途 | ORDER BY例 | 説明 |
---|---|---|
部署別年齢順 | department, age DESC | 部署で分けて年齢の高い順 |
優先度付きタスク | CASE priority WHEN… | カスタム順序で並び替え |
売上ランキング | total_sales DESC, date | 売上高順、同額なら日付順 |
在庫管理 | stock=0, category, price | 在庫切れを最後、カテゴリ別価格順 |
コメント