我论坛中的帖子都有个人永久链接。
帖子是分页的,用户可以按日期和评级对其进行排序。
当访问永久链接时,由于分页,需要计算帖子所在的页面。
这些帖子在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查询实现相同的效果?
答案 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等进行优化的查询。