web-dev-qa-db-ja.com

MySQL LIMIT、OFFSETを使用したページネーション

ページごとに4項目のみを表示するようにデータを制限するコードがあります。使用している列には20〜30個のアイテムがあるので、それらをページ全体に広げる必要があります。

最初のページには、次のものがあります。

    $result = mysqli_query($con,"SELECT * FROM menuitem LIMIT 4");
{
  echo "<tr>";
  echo "<td align='center'><img src=\"" . $row['picturepath'] . "\" /></td>";
  echo "<td align='center'>" . $row['name'] . "</td> <td align='center'> <input type='button' value='More Info'; onclick=\"window.location='more_info.php?';\"> </td>";
  echo "<td align='center'>" . $row['price'] . "</td> <td align='center'> <input type='button' value='Add to Order' onclick=''> </td>";
  echo "</tr>";
 }
echo "</table>";

mysqli_close($con);

    ?> 

    <table width="1024" align="center" >
        <tr height="50"></tr>
            <tr>
                <td width="80%" align="right">
                    <a href="itempage2.php">NEXT</a>
                </td>
                <td width="20%" align="right">
                    <a href="">MAIN MENU</a>
                </td>
            </tr>
    </table>

ページの下部にある2つ目のページ「itempage2.php」がアンカータグ内にあることに気付くでしょう。アイテムページ2には、selectステートメントにオフセット4がリストされていることを除いて、同じコードがあります。

$result = mysqli_query($con,"SELECT * FROM menuitem LIMIT 4 offset 4");

これは、データベース内に所定の数のアイテムがある場合に、このように機能します。しかし、それはそれほど良くありません。新しいページを作成する必要があるのは、現在のようにハードコードされていないアイテムがさらにある場合のみです。

新しいページをそれぞれハードコーディングし、オフセットすることなく、複数のページを作成するにはどうすればよいですか?

29
ValleyDigital

まず、ページごとに個別のサーバースクリプトを用意しないでください。これは単なる狂気です。ほとんどのアプリケーションは、URLでページネーションパラメータを使用してページネーションを実装します。何かのようなもの:

http://yoursite.com/itempage.php?page=2

リクエストされたページ番号にアクセスするには、$_GET['page']

これにより、SQLの定式化が非常に簡単になります。

// determine page number from $_GET
$page = 1;
if(!empty($_GET['page'])) {
    $page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT);
    if(false === $page) {
        $page = 1;
    }
}

// set the number of items to display per page
$items_per_page = 4;

// build query
$offset = ($page - 1) * $items_per_page;
$sql = "SELECT * FROM menuitem LIMIT " . $offset . "," . $items_per_page;

たとえば、ここでの入力がpage=2、ページごとに4行の場合、クエリは "

SELECT * FROM menuitem LIMIT 4,4

それがページネーションの基本的な問題です。これで、ページの総数を理解したいという追加要件があります(「次のページ」を表示するか、リンクを介してページXへの直接アクセスを許可するかを決定できるようにするため)。

これを行うには、テーブルの行数を理解する必要があります。

実際に制限されたレコードセットを返す前に、DB呼び出しでこれを行うことができます(要求されたページが存在することを明らかに検証したいので、前に言います)。

これは実際には非常に簡単です:

$sql = "SELECT your_primary_key_field FROM menuitem";
$result = mysqli_query($con, $sql);
if(false === $result) {
   throw new Exception('Query failed with: ' . mysqli_error());
} else {
   $row_count = mysqli_num_rows($result);
   // free the result set as you don't need it anymore
   mysqli_free_result($result);
}

$page_count = 0;
if (0 === $row_count) {  
    // maybe show some error since there is nothing in your table
} else {
   // determine page_count
   $page_count = (int)ceil($row_count / $items_per_page);
   // double check that request page is in range
   if($page > $page_count) {
        // error to user, maybe set page to 1
        $page = 1;
   }
}

// make your LIMIT query here as shown above


// later when outputting page, you can simply work with $page and $page_count to output links
// for example
for ($i = 1; $i <= $page_count; $i++) {
   if ($i === $page) { // this is current page
       echo 'Page ' . $i . '<br>';
   } else { // show link to other page   
       echo '<a href="/menuitem.php?page=' . $i . '">Page ' . $i . '</a><br>';
   }
}
81
Mike Brant

つかいます .. LIMIT :pageSize OFFSET :pageStart

どこ :pageStartはthe_page_index(つまり、最初のページの場合は0)* number_of_items_per_pages(例:4)および:pageSizeはnumber_of_items_per_pagesにバインドされます。

「ページがさらにある」ことを検出するには、 SQL_CALC_FOUND_ROWS を使用するか、.. LIMIT :pageSize OFFSET :pageStart + 1および最後の(pageSize + 1)レコードの欠落を検出します。言うまでもなく、インデックスが0より大きいページには、前のページが存在します。

ページインデックス値がURLに埋め込まれている場合(「前のページ」および「次のページ」リンクなど)、適切な$_GETアイテム。

4
user2864740

シンプルに保ちたい場合は、これを試してみてください。

$page_number = mysqli_escape_string($con, $_GET['page']);
$count_per_page = 20;
$next_offset = $page_number * $count_per_page;
$cat =mysqli_query($con, "SELECT * FROM categories LIMIT $count_per_page OFFSET $next_offset");
while ($row = mysqli_fetch_array($cat))
        $count = $row[0];

後は君しだい。 2つのテーブルからの結果がある場合は、別のアプローチを試してみることをお勧めします。

4

OFFSETを使用する場合、12ページは大した問題ではありません。しかし、何百ページもある場合、OFFSETはパフォーマンスに悪いことがわかります。これは、スキップされるすべての行を毎回読み取る必要があるためです。

中断した場所を覚えておいてください

2
Rick James