MySQLデータ-ページングを実装する最良の方法?


209

iPhoneアプリをPHP Webサービスに接続して、MySQLデータベースからデータを取得します。リクエストは500件の結果を返すことができます。

ページングを実装して一度に20項目を取得する最良の方法は何ですか?

データベースから最初の20個の広告を受け取ったとします。次の20個の広告をリクエストするにはどうすればよいですか?

回答:


310

MySQLのドキュメントから

LIMIT句を使用すると、SELECTステートメントによって返される行数を制限できます。LIMITは1つまたは2つの数値引数を取ります。これらは両方とも負でない整数定数でなければなりません(準備済みステートメントを使用する場合を除く)。

2つの引数を使用する場合、最初の引数は返す最初の行のオフセットを指定し、2番目は返す行の最大数を指定します。最初の行のオフセットは0(1ではない)です。

SELECT * FROM tbl LIMIT 5,10;  # Retrieve rows 6-15

特定のオフセットから結果セットの終わりまでのすべての行を取得するには、2番目のパラメーターに大きな数を使用できます。次のステートメントは、96行目から最後のすべての行を取得します。

SELECT * FROM tbl LIMIT 95,18446744073709551615;

引数が1つの場合、値は結果セットの先頭から返す行数を指定します。

SELECT * FROM tbl LIMIT 5;     # Retrieve first 5 rows

つまり、LIMIT row_countはLIMIT 0、row_countと同等です。


108
ページングにLIMITを使用する場合は、ORDER BYも指定する必要があります。
Mark Byers、

10
@shylent:ドキュメントの引用には何の問題もありませんが、ドキュメントをコピーしていて、元のソースへのリンクを提供していることを彼が言及していたはずであることに同意します。また、ドキュメントにORDER BYを使用せずにLIMITを使用する例が含まれていることにも驚いています。ORDER BYがないと、呼び出し間で順序が同じになる保証はありません。
マークバイアーズ

13
(?何ページネーションのためのものであり、その者-権利、小さなチャンクに大きな結果セット壊す)、大きな結果セットをページ付けたときに、とにかく、あなたは、あなたがしなければということを覚えておいてくださいlimit X, Y、どのような基本的に起こることはX + Y行が検索されることで、その後、最初からX行が削除され、残っているものが返されます。繰り返します:limit X, YX + Y行のスキャンになります。
10

7
私はあなたのLIMIT 95、18446744073709551615アイデア好きではない...見てみましょうOFFSET;-)
CharlesLeaf

5
大きなデータを扱う場合、これは効率的ではありません。codular.com/implementing-paginationで、特定のscenerioに適した複数の方法を確認してください。
2017年

125

500レコードの場合、効率はおそらく問題ではありませんが、数百万のレコードがある場合は、WHERE句を使用して次のページを選択すると便利です。

SELECT *
FROM yourtable
WHERE id > 234374
ORDER BY id
LIMIT 20

ここの「234374」は、前に表示したページの最後のレコードのIDです。

これにより、idのインデックスを使用して最初のレコードを検索できます。使用LIMIT offset, 20すると、最後に向かってページを移動するにつれて、速度が遅くなることがわかります。前述のように、レコードが200個しかない場合はおそらく問題にはなりませんが、結果セットが大きくなると、違いが生じる可能性があります。

このアプローチのもう1つの利点は、呼び出し間でデータが変更されても、レコードを見逃したり、繰り返しレコードを取得したりしないことです。これは、行の追加または削除は、変更後のすべての行のオフセットを意味するためです。あなたの場合、それはおそらく重要ではありません-私はあなたの広告のプールがあまり頻繁に変化しないと思います、とにかく誰も彼らが同じ広告を2回続けて得ても気付かないでしょう-しかしあなたが「最善の方法」を探しているなら次に、これは、使用するアプローチを選択するときに留意すべきもう1つのことです。

オフセットでLIMITを使用したい場合(ユーザーがページを1つずつページングするのではなく、ページ10000に直接移動する場合に必要です)、遅い行のルックアップに関するこの記事を読んで、大きなLIMITのパフォーマンスを向上させることができます。オフセット。


1
これはそれに似ています:P私はその含意を絶対に承認しませんが、「新しい」IDは常に「古い」IDよりも大きいということですが、ほとんどの場合、これは実際に当てはまるので、これは「良い」と思います足りる'。とにかく、はい、あなたが示したように、適切なページネーション(大きな結果セットでの深刻なパフォーマンス低下なし)は特に簡単ではなくlimit 1000000, 10、それが機能することを期待しても、どこにも行けません。
10

1
遅延検索リンクは非常に
便利です

1
IDの順序付けに「DESC」を使用した場合、この改ページは逆方向に機能します。私はそれが好きです!
Dennis Heiden

2
しかし、実際には、ID、またはほのめかしによる「作成日」で注文する頻度はどれくらいですか。
RichieHH 2017年

area=width*height重要なのは投稿ですが、重要なのはレコードの数だけではなく、各レコードのサイズも結果をメモリに保存する際の要素です
必要ありません

43

クエリのOFFSETを定義します。例えば

ページ1-(レコード01〜10):オフセット= 0、制限= 10;

2ページ-(レコード11〜20)オフセット= 10、制限= 10;

次のクエリを使用します。

SELECT column FROM table LIMIT {someLimit} OFFSET {someOffset};

ページ2の例:

SELECT column FROM table
LIMIT 10 OFFSET 10;

1
ページ2のオフセット= 10を意味しませんか?
Jenna Maiz 2017

28

それについての文献があります:

大きなOFFSETsを使用すると主な問題が発生します。条項での範囲の選択から、ある種のキャッシュや事前計算ページOFFSETまで、さまざまな手法での使用を避けます。idWHERE

Use the INDEX、Lukeに推奨される解決策があります。


1
複雑なクエリの各ページングクエリの最大IDを取得すると、実際的でなく、プロダクション以外の使用法ではランク、行番号、ページ間の句タイプのページングがパフォーマンスに役立ちます。
リズワンパテル2017

その戦略は考慮に入れられ、提供されたリンクで適切に評価されます。それほど簡単ではありません。
Luchostein 2017年

提供されたリンクは、基本ピボットユニピボット、クロスアプライ、マルチCTE、または派生テーブルメカニクスを満たすように思われますか?繰り返しますが、maxidを取得するために、このような規模のクエリを再度書き換えることで私のケースを待機しています。その後、並べ替えと並べ替え順序を使用したn "列の列の順列と組み合わせ!
Rizwan Patel

1
「ページ分割は正しい方法で行われた」というリンクを誤解しているのでしょうか、それとも、フィルタリングを必要とするクエリでは単に実際的ではないのでしょうか。
contactmatt

1
@contactmatt私はあなたの不安を共有します。結局のところ、完全な要件を効率的に実装する方法はないようですが、元の要件を緩和したバリエーションです。
Luchostein


6

あなたもできる

SELECT SQL_CALC_FOUND_ROWS * FROM tbl limit 0, 20

selectステートメントの行数(制限なし)は同じselectステートメントでキャプチャされるため、テーブルサイズを再度クエリする必要はありません。SELECT FOUND_ROWS();を使用して行数を取得します。


1
これは特に非効率的です。*必要であるよりも多くの列の結果をフェッチし、SQL_CALC_FOUND_ROWSそれらの列の結果は、から読み出される全てそれらが結果に含まれていなくても、テーブル内の行。これらの列をすべて読み取らない別のクエリで行数を計算する方がはるかに効率的です。次に、20行を読み取った後、メインクエリを停止できます。
thomasrutter

本気ですか?大きなテーブルSQL_CALC_FOUND_ROWSに対するクエリと、使用していない別のクエリの時間を測定しました。時差はありませんでした。2つのクエリを実行するよりも高速です。1-有効な制限0 20から*を選択し、次に有効な値からcount(*)を選択します。
surajz

1
はい、きっと- 詳細はこちらです。インデックスを使用して行をフィルタリングする場合は常に、SQL_CALC_FOUND_ROWSは2つの個別のクエリを実行するよりも大幅に遅くなります。まれに、インデックスを使用しない場合、または(この簡略化された例のように)WHERE句がなく、それがMYISAMテーブルである場合、ほとんど違いがありません(ほぼ同じ速度です)。
thomasrutter


4

クエリ1: SELECT * FROM yourtable WHERE id > 0 ORDER BY id LIMIT 500

クエリ2: SELECT * FROM tbl LIMIT 0,500;

クエリ1は、小規模または中規模のレコードでより高速に実行されます。レコード数が5,000以上の場合、結果は類似しています。

500レコードの結果:

Query1は9.9999904632568ミリ秒かかります

Query2は19.999980926514ミリ秒かかります

8,000レコードの結果:

Query1は129.99987602234ミリ秒かかります

Query2は160.00008583069ミリ秒かかります


にインデックスを付ける必要がありますid
2015年

6
どのようにid > 0役立ちますか?
Michel Jung、

1
Maartenが言ったように、これらの2つのクエリは基本的に同じように見え、おそらく同じマシンレベルのコマンドに分解されます。インデックス作成の問題または本当に古いバージョンのMySQLが必要です。
HoldOffHunger

ありがとう、私はあなたの答えを見なかったように、私はちょうど、順序と制限が来る順序を見る必要がありました
Shreyan Mehta

間違った例が使用されました。offset(限界まで最初の引数がオフセットされて)、あなたはまだ、その後の間にあるセクションの復帰、そしてオフセットの量を破棄、限界までのすべてのデータを選択しているoffsetとしますlimitwhere一方、with 句では、クエリの一種の開始点を設定し、ONLYその特定の部分をクエリします。
セナプス

0

ページングは​​、単一のテーブルからデータをフェッチする場合は単純ですが、複数のテーブルを結合するデータを取得する場合は複雑です。MySqlとSpringの良い例を次に示します。https
//www.easycodeforall.com/zpagination1.jsp


いつか消えてしまうようなサードパーティのサイトへのリンクは共有しないでください。著者の質問に答えたい場合は、関連するコードを投稿して支援してください。
ブランドなしのマンチェスター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.