5.

SQL / Laravel 降順 (DESC) 並び替え完全ガイド

編集
この記事の要点
  • 降順 = DESC昇順 = ASC (省略時は ASC)
  • ORDER BY created_at DESC で「新しい順」
  • 複数列指定: ORDER BY status ASC, created_at DESC ─ 第一キーで分類、同値内で第二キー
  • Laravel: orderBy("col", "desc") / orderByDesc("col") / latest() (= created_at DESC) / oldest()
  • NULL の位置: MySQL は ASC で NULL 先頭 / DESC で末尾。PostgreSQL は NULLS FIRST/LAST で明示可
  • 降順インデックス: ORDER BY DESC を多用するなら CREATE INDEX idx ON tbl (created_at DESC)

基本構文

-- 昇順 (省略時のデフォルト)
SELECT * FROM users ORDER BY created_at ASC;
SELECT * FROM users ORDER BY created_at;     -- ASC と同じ

-- 降順 (新しい順)
SELECT * FROM users ORDER BY created_at DESC;

-- 複数列
SELECT * FROM users
ORDER BY status ASC, created_at DESC;
-- ① status の昇順で並び ② 同じ status の中では created_at の降順

-- LIMIT と組み合わせ (最新 10 件)
SELECT * FROM users ORDER BY created_at DESC LIMIT 10;

複数列ソートの実用例

用途ORDER BY
ランキング (点数高 → 同点は早い順)score DESC, finished_at ASC
掲示板 (固定スレを上、その他は新着順)is_pinned DESC, last_posted_at DESC
料金プラン (高い順、同額は人気順)price DESC, popularity DESC
ログ (時刻降順、同時刻は ID 降順で安定化)created_at DESC, id DESC

Laravel クエリビルダでの降順

use Illuminate\Support\Facades\DB;

// 方法1: orderBy
DB::table('users')->orderBy('created_at', 'desc')->get();

// 方法2: orderByDesc (Laravel 5.6+)
DB::table('users')->orderByDesc('created_at')->get();

// 方法3: latest() = orderBy(created_at, desc)
DB::table('users')->latest()->get();

// 方法4: latest('カラム名')
DB::table('articles')->latest('published_at')->get();

// oldest() は逆 (created_at ASC)
DB::table('users')->oldest()->get();

// 複数列
DB::table('articles')
    ->orderBy('is_pinned', 'desc')
    ->orderByDesc('published_at')
    ->orderByDesc('id')
    ->get();

Eloquent でも同じ

// 最新 10 件
$latest = Article::latest()->take(10)->get();

// カスタム順序
$top = Article::orderByDesc('view_count')
              ->orderBy('title')
              ->paginate(20);

// リレーション先のカラムでソートしたい場合は join 必須
$posts = Post::join('users', 'users.id', '=', 'posts.user_id')
             ->orderByDesc('users.followers_count')
             ->select('posts.*')
             ->get();

NULL の扱い

NULL は他の値と比較不能なので、DB ごとに位置が異なります。

DBASC のとき NULL はDESC のとき NULL は明示指定
MySQL / MariaDB先頭末尾非対応 (擬似的に ORDER BY col IS NULL, col)
PostgreSQL末尾先頭NULLS FIRST / NULLS LAST
Oracle末尾先頭NULLS FIRST / NULLS LAST
SQL Server先頭末尾非対応 (CASE WHEN で擬似)
-- PostgreSQL: NULL を最後に
SELECT * FROM products
ORDER BY price DESC NULLS LAST;

-- MySQL: 同じ意味を擬似的に
SELECT * FROM products
ORDER BY price IS NULL, price DESC;
-- price IS NULL が 0 (= NULL でない) のものが先に並び、内部で price DESC

インデックスとパフォーマンス

ORDER BY は対応するインデックスがあるとソート処理が不要になり高速です。

-- 単純降順
CREATE INDEX idx_created_at ON articles (created_at);
-- MySQL は B-Tree インデックスを両方向に読めるので、
-- ORDER BY created_at DESC でも DESC キーを作る必要は基本ない

-- ただし複合インデックスで方向が混在すると効かない
CREATE INDEX idx_complex ON articles (status ASC, created_at DESC);
-- ↑ MySQL 8.0+ で意味あり (Descending Index 対応)
-- それ以前は文法は通るが内部は ASC 扱い

-- LIMIT と組み合わせるとさらに恩恵大
SELECT * FROM articles
ORDER BY created_at DESC
LIMIT 20;
-- インデックスの末尾 20 件を読むだけで完了

EXPLAIN で確認したとき Using filesort が出るとソート処理が走っています。ない状態が理想です。

よくあるミス

  • ORDER BY DESC created_at と書く → 文法エラー。カラム名の後に DESC を置く。
  • SELECT DISTINCT name FROM users ORDER BY created_at DESC → DISTINCT 対象に created_at が無くエラーになる DB あり (PostgreSQL)
  • UNION 全体に ORDER BY を効かせたい場合はUNION の最後に書く。各 SELECT 内側の ORDER BY は無視される
  • 並び順が日々変わる: 第二キーで安定化 (created_at DESC, id DESC)

FAQ

Q: 文字列カラムで「あ → ん」順にしたい
A: 照合順序 (collation) 依存。MySQL なら utf8mb4_ja_0900_as_cs、SQL Server なら Japanese_CI_AS で日本語ソートが期待通りに。

Q: 数字 7 桁の文字列が並び順おかしい
A: 文字列ソートなので "10" < "9" になる。ORDER BY CAST(col AS UNSIGNED) で数値化、または LPAD で桁を揃える。

Q: ランダム順
A: MySQL ORDER BY RAND()、PostgreSQL ORDER BY RANDOM()。大量データでは激重なので OFFSET FLOOR(RAND() * COUNT()) で擬似化を推奨。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. MySQL/MariaDBへの接続
  2. sqliteへの接続
  3. SELECT, INSERT, UPDATE, DELETE
  4. 素のSQLを直接実行する方法
  5. Order by DESCの指定方法
  6. limit, offsetの指定方法
  7. filterの検索オプション
  8. django-filterのlookup_expr検索オプション
  9. モデルの内部結合(1対1)

最近更新/作成されたページ