タイトル: Order by DESCの指定方法
SEOタイトル: SQL / Laravel 降順 (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 ごとに位置が異なります。
| DB | ASC のとき 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()) で擬似化を推奨。