【问题标题】:creating an efficient pagination system创建高效的分页系统
【发布时间】:2011-09-09 20:45:03
【问题描述】:

所以我正在为类似博客的系统创建一个分页系统。我已经有了使用 limit,offset 来限制查询的整个概念。但是,在创建实际导航时遇到了一个问题。

我不知道如何计算我的总页数。现在,显然我可以运行一个简单地选择所有行数的查询,但是,我觉得这似乎不是最有效的方法。

无论如何我可以提取原始查询中的总行数吗?

 $this->DB->build(  array('select'   => 'a.*', 
                     'from'     => array( 'ccs_custom_database_1' => 'a' ),
                      'order' => 'field_3 DESC',
                      'where' => 'field_6=",1,"',
                      'limit' => array($page,10),

                     'add_join'  => array(
                    array(
                        'select'    => 'm.members_display_name,m.members_seo_name',
                        'from'      => array( 'members' => 'm' ),
                        'type'      => 'inner',
                        'where'     => 'a.member_id=m.member_id'


                    ), 
                    array(
                        'select'    => 'c.category_name,c.category_furl_name',
                        'from'      => array( 'ccs_database_categories' => 'c' ),
                        'type'      => 'inner',
                        'where'     => 'a.category_id=c.category_id'            
                    )),

                    ));

【问题讨论】:

  • 向我们展示在您的选择中添加COUNT(id) as total 的原始查询可能会起作用
  • 您想计算所有行而不查询数据库中的 COUNT 吗?
  • @Damien na 他想将总行数添加到他的原始查询中
  • 这个查询是在 IPB 论坛内部使用的,所以语法可能有点奇怪。然而,它是不言自明的。
  • 你需要让它独立于数据库吗?分页通常涉及许多 DBMS 特定的技巧。

标签: php sql pagination


【解决方案1】:

你有两个选择。

  1. 使用查询选择总数。
  2. 使用FOUND_ROWS()

我更喜欢第二种选择,但就性能而言,它无关紧要。

【讨论】:

  • 我也更喜欢第二种选择。不过,我花了一些时间来阅读有关性能的信息,而且它似乎取决于数据库结构和查询——正如人们所写的那样,有时SQL_CALC_FOUND_ROWS 快得多,有时——慢得多。附言作者从未说过他正在使用 MySQL...
  • 我正在使用 MySQL 抱歉。
  • 你的意思是你使用MySQL?我的帖子是关于 MySQL 的。
  • 我正在回复二进制,因为他说“P.S. 作者从未说过他正在使用 MySQL”。再次抱歉,造成混淆。
  • 在这种情况下,我会按照 Wesley van Opdorp 所说的去做 - 在您的查询中的 SELECT 之后添加 SQL_CALC_FOUND_ROWS 关键字(它会导致类似 select SQL_CALC_FOUND_ROWS field1, field2 from table limit xxx,xxx 的内容),执行该查询并然后执行SELECT FOUND_ROWS()
【解决方案2】:

您始终可以对数据进行非规范化处理。例如,Post 表可以包含CommentsCount 列,该列会随着 cmets 的添加和删除而更新。

然而,在大多数情况下,这将是一个微优化,因为很少有每个帖子都会有大量 cmets。

【讨论】:

    【解决方案3】:

    如果您的表使用 MyISAM 引擎,则运行 COUNT(*) 是一个 O(1) 操作,因为它保留了总行数的缓存值。

    即使您没有使用它,或者查询略有不同,我也怀疑执行单个 COUNT 会成为系统的瓶颈。

    如果这真的是一个大问题(我建议在一张表上进行测试,其中包含您首先期望的大量虚假数据),一种选择是采取更务实的方法它。在您的用例中,您的用户可能会有所不同,但是,请接受没有人真正使用分页。在放弃之前,您最多可以期望某人阅读 2 或 3 页。使用过滤和排序将用户想要的数据推送到第一页。

    但是,即使使用过滤和排序,您仍然可能需要显示一些分页,因此您可以采用这种方法:给定页面大小为 15 条记录,选择前 151 条记录(每页 15 * 10 页 + 1)记录并计算你得到了多少。如果数字小于该数字,则显示“第 1 页,共 8 页”或其他内容。如果数字是 151,那么你有超过 10 页,所以显示“第 1 页”。这是我在一个有大约 1200 万条记录的表上使用的一种方法,效果很好。

    【讨论】:

    • Running Count(*) 使我的查询只返回 1 行数据。 >.
    • 您当然可以使用缓存,无论是总量(数据库或其他缓存,如 memcached),还是缓存分页的输出。
    • 可能的解决方案。 IPB 使这很容易做到。但是,设置它的方式我必须选择缓存整个文件。结果,我也会被困在缓存文章中,这不是很好,因为我希望成员在发布后立即看到这些文章。另一种解决方案是为所有有分页的页面创建一个单独的文件。
    • @Forgoten:查看我的更新。此外,COUNT(*) always 仅返回 1 条记录,无论您有 0 条记录还是 1000 万条记录(忽略“分组依据”)。
    • 非常感谢。真的很感激。
    【解决方案4】:

    请使用PEAR's Pager 类。它几乎可以自动处理所有事情,您可以使用$pager->getCurrentPageID()perPage 属性获取LIMIT 子句。

    不重新发明轮子可以让您免于编码、查找错误和修复错误。

    【讨论】:

      猜你喜欢
      • 2010-11-04
      • 2018-07-12
      • 2016-02-05
      • 1970-01-01
      • 1970-01-01
      • 2010-12-29
      • 1970-01-01
      • 2021-03-16
      • 2015-06-14
      相关资源
      最近更新 更多