在应用LIMIT之前获得结果计数的最佳方法

时间:2008-10-01 03:23:34

标签: php sql postgresql window-functions sql-limit

在分析来自数据库的数据时,您需要知道将有多少页面呈现页面跳转控件。

目前,我通过运行查询两次,一次包裹在count()中确定总结果,第二次应用限制来获取当前页面所需的结果。< / p>

这似乎效率低下。有没有更好的方法来确定在应用LIMIT之前会返回多少结果?

我正在使用PHP和Postgres。

5 个答案:

答案 0 :(得分:117)

纯SQL

自2008年以来情况发生了变化。您可以使用window function在一个查询中获取完整计数和有限结果。 (引入PostgreSQL 8.4 in 2009)。

SELECT foo
     , count(*) OVER() AS full_count
FROM   bar
WHERE  <some condition>
ORDER  BY <some col>
LIMIT  <pagesize>
OFFSET <offset>

请注意,与没有总计数相比,这可能要贵得多。必须对所有行进行计数,并且仅使用匹配索引中的顶行可能的快捷方式可能不再有用 小表或full_count&lt; = OFFSET + LIMIT无关紧要。事情要大得多full_count

转角案例 :当OFFSET至少与基本查询中的行数一样大时, 没有行<退回/ em> 。所以你也没有full_count。可能的替代方案:

考虑事件序列

  1. WHERE子句(和JOIN条件,但不在此处)从基表中过滤限定行。

    GROUP BY和聚合函数将在这里。)

  2. 考虑所有符合条件的行(取决于OVER子句和函数的帧规范),应用窗口函数。简单的count(*) OVER()基于所有行。

  3. ORDER BY

    DISTINCTDISTINCT ON会在这里。)

  4. LIMIT / OFFSET根据已建立的顺序应用,以选择要返回的行。

  5. 随着表格中行数的增加,

    LIMIT / OFFSET的效率越来越低。如果您需要更好的性能,请考虑其他方法:

    获得最终计数的替代方案

    有一些完全不同的方法来获取受影响的行数( 在应用OFFSET&amp; LIMIT之前的完整计数) 。 Postgres具有内部簿记,受上一个SQL命令影响的行数。有些客户端可以访问该信息或自行计数行(如psql)。

    例如,您可以在执行SQL命令后立即检索 plpgsql 中受影响的行数:

    GET DIAGNOSTICS integer_var = ROW_COUNT;
    

    Details in the manual.

    或者您可以使用pg_num_rows in PHP。或其他客户的类似功能。

    相关:

答案 1 :(得分:5)

正如我所描述的on my blog,MySQL有一个名为SQL_CALC_FOUND_ROWS的功能。这样就无需进行两次查询,但仍然需要完整地进行查询,即使限制条款允许它提前停止。

据我所知,PostgreSQL没有类似的功能。在进行分页时要注意的一件事(使用LIMIT最常见的事情是恕我直言):执行“OFFSET 1000 LIMIT 10”意味着数据库必须至少提取 1010行如果它只给你10.一个更高效的方法是记住你为前一行(在这种情况下为1000)排序的行的值,并重写这样的查询:“... WHERE order_row&gt ; value_of_1000_th LIMIT 10“。优点是“order_row”很可能被索引(如果没有,你就会出问题)。缺点是如果在页面视图之间添加新元素,这可能会有点不同步(但话说,访问者可能无法观察到它,并且可能会带来巨大的性能提升)。

答案 2 :(得分:1)

您可以通过不每次运行COUNT()查询来减轻性能损失。缓存页数,比如再次运行查询前5分钟。除非你看到大量的INSERT,否则它应该可以正常工作。

答案 3 :(得分:0)

由于Postgres已经执行了一定数量的缓存,因此这种方法并不像看起来那么低效。绝对不会使执行时间加倍。我们的数据库层内置了计时器,所以我看到了证据。

答案 4 :(得分:-1)

看到你需要知道为了分页的目的,我建议运行一次完整的查询,将数据作为服务器端缓存写入磁盘,然后通过分页机制提供。

如果您正在运行COUNT查询以决定是否向用户提供数据(即,如果有&gt; X记录,请回复错误),您需要坚持使用COUNT方法