SQL ORDER BY複数列指定方法|並び順を自在にコントロール完全ガイド

データベース・SQL

「まず部署順、その中で年齢の高い順に並べたい」
「商品をカテゴリごとに分けて、価格の安い順に表示したい」
「同じ点数の学生を学籍番号順で並べたい」

データベースからデータを取得するとき、こうした複数の条件で並び替えをしたいことがよくあります。そんなときに使うのが、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在庫切れを最後、カテゴリ別価格順

コメント

タイトルとURLをコピーしました