从分页论坛中的帖子固定链接确定页面

时间:2013-10-08 18:32:59

标签: php mysql permalinks

我论坛中的帖子都有个人永久链接。

帖子是分页的,用户可以按日期和评级对其进行排序。

当访问永久链接时,由于分页,需要计算帖子所在的页面。

这些帖子在mysql innodb表中以自动递增ID存储。

目前,当帖子按其得分(评分)排序时,我会使用以下内容进行计算:

<?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查询实现相同的效果?

2 个答案:

答案 0 :(得分: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是:

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子句匹配的记录),因此对于大型表来说它变得非常慢。

答案 1 :(得分:1)

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

为了使此工作可预测,您必须在排序中包含至少一个唯一列作为平局。我假设您的许多帖子实际上都具有相同的评分,所以也许您应该例如ORDER BY score DESC, id DESC另外订购评分与最新评价相同的帖子(我认为这对论坛来说可能是有意义的。)< / p>

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

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

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