SQLでORDER BYとLIMITを組み合わせて上位n件のデータを取得する方法

データベースから特定の条件に合致する上位n件のデータを取得することは、多くの場面で必要とされます。SQLを使用してこれを実現するには、ORDER BY句とLIMIT句を組み合わせて使用する方法が有効です。本記事では、ORDER BYとLIMITの基本的な使い方から、実践的な応用例までを詳しく解説します。これにより、効率的にデータを取得し、業務のパフォーマンスを向上させるための知識を身につけることができます。

上位n件を安定して選ぶには、並び順と同値時の扱いを決めてから件数を制限します。LIMITはMySQL・PostgreSQL・SQLiteなどで使えますが、SQL ServerはTOP、OracleではFETCH FIRSTなどを使います。ORDER BYなしでは「最初の行」の順序は保証されません。

以降の基本例は、ID列が一意で、並べ替え対象の数値がNULLではない前提です。salesは製品ごとに集計済みの1行、customers.total_purchase_amountも顧客ごとの集計済み金額を想定します。明細行から製品・顧客の上位を求める場合は、先にGROUP BYで必要な単位へ集計してください。各例は独立したテーブル例で、後半に実行可能なサンプルを載せます。

目次

ORDER BYの基本構文と使用例

SQLのORDER BY句は、クエリの結果を特定の列を基準に並び替えるために使用されます。これにより、結果セットの行を昇順または降順で表示できます。

ORDER BYの基本構文

ORDER BY句の基本構文は次の通りです:

SELECT column1, column2, ...
FROM table_name
ORDER BY column1 [ASC|DESC], column2 [ASC|DESC], ...;
  • column1, column2, ...:並び替えの基準となる列
  • ASC:昇順(デフォルト)
  • DESC:降順

ORDER BYの使用例

例えば、従業員データベースから年齢順に並び替えたリストを取得する場合、次のようなクエリを使用します:

SELECT employee_id, name, age
FROM employees
ORDER BY age ASC, employee_id ASC;

このクエリは、従業員を年齢の昇順で表示します。

複数列での並び替え

複数の列で並び替えを行う場合は、次のように記述します:

SELECT employee_id, name, age, salary
FROM employees
ORDER BY age DESC, salary ASC, employee_id ASC;

このクエリは、まず年齢を降順で並び替え、その後に給与を昇順で並び替えます。

LIMITの基本構文と使用例

SQLのLIMIT句は、クエリで取得する行数を制限するために使用されます。返す行数は抑えられますが、内部で読み込む行数やソート負荷まで同じ件数になるとは限りません。

LIMITの基本構文

LIMIT句の基本構文は次の通りです:

SELECT column1, column2, ...
FROM table_name
LIMIT number_of_rows;
  • number_of_rows:取得する行数を指定します。

LIMITの使用例

例えば、並び順を指定せず最大5行だけ取得する構文は次のとおりです。特定の「最初の5人」を選ぶ用途には、このまま使わないでください:

SELECT employee_id, name, age
FROM employees
LIMIT 5;

このクエリは最大5行を返しますが、どの5行か・どの順序かは保証されません。上位やページ分割ではORDER BYと一意キーを指定します。

OFFSETと組み合わせたLIMITの使用

OFFSETを使用すると、取得する行の開始位置を指定できます。例えば、6行目から10行目までのデータを取得する場合は次のように記述します:

SELECT employee_id, name, age
FROM employees
ORDER BY employee_id ASC
LIMIT 5 OFFSET 5;

employee_idの昇順で5行を飛ばし、その次を最大5行返します。残りが5行未満なら結果も少なくなります。OFFSETは0始まりの「飛ばす行数」です。

ORDER BYとLIMITを組み合わせたクエリの実行方法

ORDER BYとLIMITを組み合わせることで、特定の基準で並び替えた上位n件のデータを効率的に取得できます。これは、ランキング表示やトップnのデータを取得する場合に非常に有用です。

基本的な組み合わせ方

ORDER BYとLIMITを組み合わせた基本的なクエリの構文は次の通りです:

SELECT column1, column2, ...
FROM table_name
ORDER BY column1 [ASC|DESC]
LIMIT number_of_rows;
  • ORDER BY column1 [ASC|DESC]:並び替え基準
  • LIMIT number_of_rows:取得する行数

使用例:売上トップ5の製品を取得する

例えば、売上データベースから売上額の上位5件の製品を取得する場合、次のようなクエリを使用します:

SELECT product_id, product_name, sales_amount
FROM sales
ORDER BY sales_amount DESC, product_id ASC
LIMIT 5;

このクエリは、売上額が最も多い上位5件の製品を降順で表示します。

複数の基準での並び替えとLIMITの使用

複数の基準で並び替えた上で、上位n件を取得する場合の例です。例えば、最も売れた製品をカテゴリ別に並び替えてから上位5件を取得する場合:

SELECT category, product_id, product_name, sales_amount
FROM sales
ORDER BY category ASC, sales_amount DESC, product_id ASC
LIMIT 5;

これはカテゴリ順を優先した結果全体から最大5行を返すクエリです。カテゴリごとに5件ではなく、先頭カテゴリだけで5行に達する場合もあります。各カテゴリの上位を取るには、後述のPARTITION BYを使います。

応用例:上位n件のデータを取得する実用的なシナリオ

ORDER BYとLIMITを使用することで、さまざまな実用的なシナリオで上位n件のデータを効率的に取得できます。以下にいくつかの応用例を紹介します。

顧客の購買履歴から上位n件の高額購入者を抽出

例えば、最も高額な購入をした上位10人の顧客を抽出する場合:

SELECT customer_id, customer_name, total_purchase_amount
FROM customers
ORDER BY total_purchase_amount DESC, customer_id ASC
LIMIT 10;

このクエリは、購買履歴から高額購入順に顧客を並び替え、上位10人のデータを表示します。

従業員のパフォーマンス評価から上位n件を取得

従業員のパフォーマンススコアを基に上位5人の優秀な従業員を取得する場合:

SELECT employee_id, employee_name, performance_score
FROM employees
ORDER BY performance_score DESC, employee_id ASC
LIMIT 5;

このクエリは、従業員のパフォーマンススコアを降順で並び替え、上位5人のデータを表示します。

商品の売上ランキングを表示

例えば、特定のカテゴリで売上が多い上位5件の製品を取得する場合:

SELECT product_id, product_name, sales_amount
FROM products
WHERE category = 'Electronics'
ORDER BY sales_amount DESC, product_id ASC
LIMIT 5;

このクエリは、エレクトロニクスカテゴリの製品を売上順に並び替え、上位5件のデータを表示します。

ブログ記事の人気ランキングを取得

ブログサイトで、最も閲覧された上位5件の記事を取得する場合:

SELECT post_id, post_title, view_count
FROM blog_posts
ORDER BY view_count DESC, post_id ASC
LIMIT 5;

このクエリは、記事の閲覧数を基に上位5件の人気記事を表示します。

SQLのパフォーマンス最適化

ORDER BYとLIMITを使用する際、パフォーマンスを最適化することが重要です。適切なインデックスの使用やクエリの最適化により、データ取得の速度を向上させることができます。

インデックスの活用

並べ替えや絞り込みに合うインデックスは高速化の候補ですが、追加すれば必ず速くなるわけではありません。例えば単一列索引は次のように作れますが、本記事の同値時のID順やWHERE条件まで含めた実行計画を確認してください:

CREATE INDEX idx_sales_amount ON sales(sales_amount);

索引が使われるか、追加のソートが必要かは実行計画で確認します。索引には書き込み負荷と容量のコストがあるため、無条件に追加しません。

クエリプランの確認と最適化

EXPLAINコマンドを使用してクエリプランを確認し、どのようにクエリが実行されるかを把握します。これにより、ボトルネックを特定し、最適化の方向性を決めることができます:

EXPLAIN SELECT product_id, product_name, sales_amount
FROM sales
ORDER BY sales_amount DESC, product_id ASC
LIMIT 10;

この例はMySQL・PostgreSQLのEXPLAIN構文です。SQLiteの見やすい計画表示にはEXPLAIN QUERY PLAN、SQL Serverでは同製品の実行計画機能を使います。ANALYZE付きなど、実際にクエリを実行する方式もあるため、本番の負荷と副作用を確認してから使います。

サブクエリの利用

サブクエリは処理の目的を分けて表現する方法で、使うだけで負荷が減るとは限りません。次は最も売上が多いカテゴリを1つ選び、そのカテゴリの上位商品を取得する例です。salesとproductsの集計期間・金額定義が一致する前提です:

SELECT product_id, product_name, sales_amount
FROM products
WHERE category = (
  SELECT category
  FROM sales
  GROUP BY category
  ORDER BY SUM(sales_amount) DESC, category ASC
  LIMIT 1
)
ORDER BY sales_amount DESC, product_id ASC
LIMIT 5;

このクエリは、最も売上が多いカテゴリを特定し、そのカテゴリのトップ5商品を取得します。

演習問題:ORDER BYとLIMITを使ったSQLクエリ

学んだ内容を確認するために、以下の演習問題を解いてみましょう。これらの問題は、ORDER BYとLIMITの理解を深めるのに役立ちます。

問題1:従業員の年齢順に並び替え、最年長の5人を取得

従業員テーブルから、年齢の降順で並び替え、最年長の5人のデータを取得するクエリを書いてください。

SELECT employee_id, name, age
FROM employees
ORDER BY age DESC, employee_id ASC
LIMIT 5;

問題2:製品の売上ランキングを取得

製品テーブルから、売上額の多い順に並び替え、上位10件の製品を取得するクエリを書いてください。

SELECT product_id, product_name, sales_amount
FROM products
ORDER BY sales_amount DESC, product_id ASC
LIMIT 10;

問題3:特定のカテゴリの上位3件の製品を取得

カテゴリが「Books」である製品の中から、売上額の多い順に並び替え、上位3件のデータを取得するクエリを書いてください。

SELECT product_id, product_name, sales_amount
FROM products
WHERE category = 'Books'
ORDER BY sales_amount DESC, product_id ASC
LIMIT 3;

問題4:顧客の購入履歴から上位5件を取得

顧客テーブルから、購入金額の多い順に並び替え、上位5人の顧客を取得するクエリを書いてください。

SELECT customer_id, customer_name, total_purchase_amount
FROM customers
ORDER BY total_purchase_amount DESC, customer_id ASC
LIMIT 5;

演習問題の解答と解説

ここでは、先ほどの演習問題の解答とその解説を示します。各問題のSQLクエリを確認しながら、理解を深めてください。

問題1:従業員の年齢順に並び替え、最年長の5人を取得

SELECT employee_id, name, age
FROM employees
ORDER BY age DESC, employee_id ASC
LIMIT 5;

このクエリは、従業員のデータを年齢の降順で並び替え、最も年長な5人の従業員を取得します。ORDER BYで年齢を降順にし、LIMITで上位5件を指定しています。

問題2:製品の売上ランキングを取得

SELECT product_id, product_name, sales_amount
FROM products
ORDER BY sales_amount DESC, product_id ASC
LIMIT 10;

このクエリは、製品の売上額を降順で並び替え、売上が多い上位10件の製品を取得します。ORDER BYで売上額を降順にし、LIMITで上位10件を指定しています。

問題3:特定のカテゴリの上位3件の製品を取得

SELECT product_id, product_name, sales_amount
FROM products
WHERE category = 'Books'
ORDER BY sales_amount DESC, product_id ASC
LIMIT 3;

このクエリは、カテゴリが「Books」である製品を対象に売上額を降順で並び替え、上位3件の製品を取得します。WHERE句でカテゴリを指定し、ORDER BYで売上額を降順に、LIMITで上位3件を指定しています。

問題4:顧客の購入履歴から上位5件を取得

SELECT customer_id, customer_name, total_purchase_amount
FROM customers
ORDER BY total_purchase_amount DESC, customer_id ASC
LIMIT 5;

このクエリは、顧客の購入金額を降順で並び替え、購入金額が多い上位5人の顧客を取得します。ORDER BYで購入金額を降順にし、LIMITで上位5件を指定しています。

再現用データで確認:同順位・カテゴリ別・NULL・ページ分割

同名のテーブルがない空の検証用データベースで使う学習用サンプルです。MySQL・PostgreSQL・SQLiteのLIMIT構文を使います。RANK・ROW_NUMBERにはウィンドウ関数をサポートするバージョンが必要です。

CREATE TABLE topn_demo (
  product_id INTEGER PRIMARY KEY,
  product_name VARCHAR(30) NOT NULL,
  category VARCHAR(30) NOT NULL,
  sales_amount INTEGER
);
INSERT INTO topn_demo VALUES
 (1,'Book A','Books',100),
 (2,'Book B','Books',90),
 (3,'Book C','Books',90),
 (4,'Book D','Books',80),
 (5,'Device A','Electronics',120),
 (6,'Device B','Electronics',110),
 (7,'Device C','Electronics',110),
 (8,'Device D','Electronics',NULL);

全体から最大2件を安定して取得する

SELECT product_id, product_name, sales_amount
FROM topn_demo
WHERE sales_amount IS NOT NULL
ORDER BY sales_amount DESC, product_id ASC
LIMIT 2;

結果はID 5・6の2行です。同額110のID 6と7は、IDの小さい6を先に選びます。LIMIT 2は「2位と同額を全員含める」という指定ではありません。

同額を含む上位2件の境界を残す

SELECT product_id, product_name, sales_amount, sales_rank
FROM (
  SELECT product_id, product_name, sales_amount,
         RANK() OVER (ORDER BY sales_amount DESC) AS sales_rank
  FROM topn_demo
  WHERE sales_amount IS NOT NULL
) ranked
WHERE sales_rank <= 2
ORDER BY sales_amount DESC, product_id ASC;

結果はID 5・6・7の3行です。RANKのORDER BYにproduct_idを入れると同額が同順位ではなくなります。表示順のIDは外側で指定します。「異なる売上額の上位2種類」が目的ならDENSE_RANKです。順位が飛ぶRANKとは同値がある場合の意味が異なります。

カテゴリごとに最大2件を取得する

SELECT category, product_id, product_name, sales_amount
FROM (
  SELECT category, product_id, product_name, sales_amount,
         ROW_NUMBER() OVER (
           PARTITION BY category
           ORDER BY sales_amount DESC, product_id ASC
         ) AS rn
  FROM topn_demo
  WHERE sales_amount IS NOT NULL
) ranked
WHERE rn <= 2
ORDER BY category ASC, sales_amount DESC, product_id ASC;

BooksはID 1・2、ElectronicsはID 5・6となり、合計4行です。各カテゴリで境界の同額も含めるなら、ROW_NUMBERをRANKへ変え、ウィンドウ内のORDER BYから一意キーを外すなど、順位の定義を変えます。ウィンドウ関数の対応版を確認してください。

NULLを最後に並べたい場合

SELECT product_id, sales_amount
FROM topn_demo
ORDER BY CASE WHEN sales_amount IS NULL THEN 1 ELSE 0 END,
         sales_amount DESC, product_id ASC
LIMIT 8;

結果のID順は5・6・7・1・2・3・4・8です。NULLの既定の並び順はDBMSで異なるため、除外するのか最後に置くのかを明示します。NULLを勝手に売上0へ置き換えると、未集計と実際の0を混同します。

大きなOFFSETの代わりに直前のキーを使う

SELECT product_id, product_name, sales_amount
FROM topn_demo
WHERE sales_amount IS NOT NULL
  AND (sales_amount < 110
       OR (sales_amount = 110 AND product_id > 7))
ORDER BY sales_amount DESC, product_id ASC
LIMIT 3;

最初の3行がID 5・6・7だった場合、その最後のキー(110, 7)より後を取る例です。次はID 1・2・3になります。アプリでは直前の値をパラメーターとして渡します。降順の金額には<、昇順のIDには>を使う点に注意してください。

OFFSETが大きいと、飛ばす行を内部で処理する負荷が増えることがあります。キーを使うページ分割も常に速いわけではなく、索引と実行計画を確認します。どちらの方式でも、閲覧中に売上額が更新された場合の重複・欠落を完全には防げません。必要なら同じスナップショットや集計時点を維持する設計にします。

SQL ServerとOracleでは構文を置き換える

同じtopn_demoで最大2件を選ぶ場合の構文例です。SQL ServerでLIMITは使いません。

-- SQL Server
SELECT TOP (2) product_id, product_name, sales_amount
FROM topn_demo
WHERE sales_amount IS NOT NULL
ORDER BY sales_amount DESC, product_id ASC;

Oracle 12c以降など、行数制限句をサポートする版では次の形です。古いOracleのROWNUMで、並び替え前に行数を制限する書き方とは区別してください。

-- Oracle
SELECT product_id, product_name, sales_amount
FROM topn_demo
WHERE sales_amount IS NOT NULL
ORDER BY sales_amount DESC, product_id ASC
FETCH FIRST 2 ROWS ONLY;

SQL ServerのTOP (2) WITH TIESやOracleのFETCH FIRST 2 ROWS WITH TIESを使う場合、同順位はORDER BYの全項目で判定されます。売上の同額を含めたいのに一意IDまで並べ替えキーへ入れると、同額が同順位として扱われなくなる点に注意してください。

再現用データと上記のLIMIT/ウィンドウ関数の5つのSELECTはSQLite 3.53.1で出力を確認しました。SQL Server・Oracleの例や他エンジンの性能を実行検証したものではありません。

まとめ

ORDER BYとLIMITを組み合わせることで、SQLクエリで上位n件のデータを効率的に取得する方法について解説しました。ORDER BY句はデータを特定の基準で並び替えるために使用し、LIMIT句は取得する行数を制限します。これにより、ランキング表示やトップnのデータ抽出など、さまざまなシナリオで活用できます。

パフォーマンスの最適化を行うためには、インデックスの設定やクエリプランの確認が重要です。また、応用例や演習問題を通じて実際の使用方法を学びました。これらの知識を活用して、効率的なデータベース操作を行いましょう。

公式リファレンス

参照:PostgreSQLのLIMITとOFFSET、MySQLのLIMIT最適化と同値時の順序、ウィンドウ関数、SQL ServerのTOP、OracleのSELECTと行数制限句。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次