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 回答
1

我刚刚想到了一个完全不同的方法,所以我将它添加为第二个答案,而不是编辑第一个。

为此,您始终必须在排序中包含至少一个唯一列作为决胜局。我假设您的许多帖子实际上将具有相同的评级,所以也许您应该例如ORDER BY score DESC, id DESC另外订购与最新的评级相同的帖子(我认为这对论坛来说无论如何都是有意义的)。

然后对于上面提到的排序顺序,您可以使用以下查询获取在相关帖子之前排序的帖子数量:

SELECT COUNT(1) FROM topic_posts
  WHERE topic_id = $topic_id
        AND ((score > $post_score) OR (score = $post_score AND id > $post_id));

这是一个查询,您可以根据需要使用 indizes 等进行优化。

于 2013-10-09T15:27:50.307 回答