0

私のフォーラムのすべての投稿には、個別のパーマリンクがあります。

投稿はページ分割されており、ユーザーは日付と評価で並べ替えることができます。

パーマリンクにアクセスすると、ページネーションのために投稿が存在するページを計算する必要があります。

投稿は、自動インクリメント ID で mysql innodb テーブルに保存されます。

現時点では、投稿がスコア (評価) で並べ替えられている場合、次の計算を使用しています。

<?php

    // These variables originate from the corresponding uri segments
    // For example: http://domain.tld/topics/[ID]/[SLUG]/[POST_ID]
    $post_id  = $uri->segment(4);
    $topic_id = $uri->segment(2);

    $post_per_page = 10;

    $query = $db->query('SELECT id FROM topic_posts WHERE topic_id = ' . $topic_id . ' ORDER BY score desc');

    foreach ($query as $key => $post)
    {
        if ($post->id == $post_id)
        {
            $postOnPage = ceil(($key + 1) / $post_per_page);
            break;
        }
    }

ただし、投稿の量は増え続けており、すべての投稿を取得するのは面倒です。

日付の並べ替えには次のクエリを使用しますが、投稿 ID が昇順ではないため、評価の並べ替えには機能しません。

SELECT CEIL((COUNT(*) + 1) / $posts_per_page) FROM topic_posts WHERE topic_id = $topic_id AND id < $post_id;

だから...どうすればphp foreachループを回避し、dbクエリで同じことを達成できますか?

4

2 に答える 2

1

クエリ内で人為的な「行番号」を生成することができます。これを使用して、投稿が表示されるページを計算できます。ただし、スキーマとテーブルのサイズによっては、このクエリのコストが非常に高くなる可能性があるため、必ずパフォーマンスを確認してください。

デモンストレーションで説明するのが一番だと思います:

最初にテーブル構造といくつかのテスト データ:

mysql> CREATE TABLE foo (a VARCHAR(10));
mysql> INSERT INTO foo VALUES ("foo"), ("bar"), ("baz");

行番号を付けて、すべての結果をある順序で返すクエリ:

mysql> SELECT f.*, @rownum := @rownum+1 AS rank FROM foo f, (SELECT @rownum := 0) r ORDER BY f.a;
+------+------+
| a    | rank |
+------+------+
| bar  |    1 |
| baz  |    2 |
| foo  |    3 |
+------+------+
3 rows in set (0.00 sec)

これを使用して、特定の行のみを選択できます。

mysql> SELECT * FROM (SELECT f.*, @rownum := @rownum+1 AS rank FROM foo f, (SELECT @rownum := 0) r ORDER BY f.a) t WHERE a = "foo";
+------+------+
| a    | rank |
+------+------+
| foo  |    3 |
+------+------+
1 row in set (0.00 sec)

基本的に、必要な結果行のみを選択する外側のクエリ内に内側の番号付けクエリをラップしています。ご覧のとおりrank、「すべての結果」の場合と同じであるため、この行番号を使用して結果ページを計算できます。

テーブル内のすべてのレコードのサブセット (たとえば、特定のフォーラム内のすべての投稿) のみをページ分割する場合、その対応する WHERE 句は、ORDER BY が次の内部の SELECT に入ります。

mysql> SELECT * FROM (SELECT f.*, @rownum := @rownum+1 AS rank FROM foo f, (SELECT @rownum := 0) r WHERE a != "baz" ORDER BY f.a) t WHERE a = "foo";
+------+------+
| a    | rank |
+------+------+
| foo  |    2 |
+------+------+
1 row in set (0.00 sec)

欠点は、実際にはすべてのレコード (内部の WHERE 句に一致するすべてのレコード) を反復処理する必要があるため、大きなテーブルでは非常に遅くなることです。

于 2013-10-08T20:46:54.460 に答える