使用LIMIT / OFFSET运行查询,并获取总行数

时间:2015-03-05 21:45:48

标签: sql postgresql count pagination limit

出于分页目的,我需要使用LIMITOFFSET子句运行查询。但是我还需要计算那个没有LIMITOFFSET子句的查询返回的行数。

我想跑:

SELECT * FROM table WHERE /* whatever */ ORDER BY col1 LIMIT ? OFFSET ?

SELECT COUNT(*) FROM table WHERE /* whatever */

同时。有没有办法做到这一点,特别是让Postgres优化它的方式,这样它比单独运行更快?

5 个答案:

答案 0 :(得分:88)

是。使用简单的窗口功能:

SELECT *, count(*) OVER() AS full_count
FROM   tbl
WHERE  /* whatever */
ORDER  BY col1
LIMIT  ?
OFFSET ?

请注意,成本将远远高于没有总数的成本,但仍然比两个单独的查询便宜。在任何一种情况下,Postgres都必须计算所有行,这会根据符合条件的行总数来计算成本。详细说明:

但是as Dani pointed out,当OFFSET至少与从基本查询返回的行数一样大时,不会返回任何行。所以我们也没有得到full_count

如果这是不可接受的,那么可能的解决方法始终返回完整计数将使用CTE和OUTER JOIN

WITH cte AS (
   SELECT *
   FROM   tbl
   WHERE  /* whatever */
   )
SELECT *
FROM  (
   TABLE  cte
   ORDER  BY col1
   LIMIT  ?
   OFFSET ?
   ) sub
RIGHT  JOIN (SELECT count(*) FROM cte) c(full_count) ON true;

如果full_count太大,则会在OFFSET后附加一行NULL值。或者它会像第一个查询一样附加到每一行。

如果包含所有NULL值的行是可能的有效结果,则必须检查offset >= full_count以消除空行原点的歧义。

这仍然只执行一次基本查询。但它增加了查询的开销,只有在重复计数的基本查询时才会付费。

如果支持最终排序顺序的索引可用,则可能需要在CTE中包含ORDER BY(冗余)。

答案 1 :(得分:0)

编辑:检索未过滤的表时,此答案有效。万一它可以帮助某人,但它可能无法完全回答最初的问题,我会允许的。

Erwin Brandstetter的答案是完美的,如果您需要一个准确的值。但是,在大桌子上,您通常只需要一个很好的近似值。 Postgres gives you just that,它会更快,因为它不需要评估每一行:

icm -cn <ip-addr> -cr <username> {
     gci -path C:\Users\IEUser\Downloads | select-string -Pattern "\d{3}-\d{4}" -AllMatches
}

实际上我不确定是将SELECT * FROM ( SELECT * FROM tbl WHERE /* something */ ORDER BY /* something */ OFFSET ? LIMIT ? ) data RIGHT JOIN (SELECT reltuples FROM pg_class WHERE relname = 'tbl') pg_count(total_count) ON true; 外部化还是像标准查询一样具有优势。应该进行一些测试。

RIGHT JOIN

答案 2 :(得分:0)

虽然 Erwin Brandstetter 的回答很有魅力,但它返回每一行的总行数,如下所示:

col1 - col2 - col3 - total
--------------------------
aaaa - aaaa - aaaa - count
bbbb - bbbb - bbbb - count
cccc - cccc - cccc - count

您可能需要考虑使用一种仅返回一次的方法,如下所示:

total - rows
------------
count - [{col1: 'aaaa'},{col2: 'aaaa'},{col3: 'aaaa'}
         {col1: 'bbbb'},{col2: 'bbbb'},{col3: 'bbbb'}
         {col1: 'cccc'},{col2: 'cccc'},{col3: 'cccc'}]

SQL 查询:

SELECT 
    (SELECT COUNT(*) FROM table) as count, 
    (SELECT json_agg(t.*) FROM (
        SELECT * FROM table
        WHERE /* whatever */
        ORDER BY col1
        OFFSET ?
        LIMIT ?
    ) AS t) AS rows 

答案 3 :(得分:-1)

不好的做法是对Just调用两次相同的查询,以获取回退结果的总行数。这将花费执行时间,并浪费服务器资源。

更好的是,您可以在查询中使用SQL_CALC_FOUND_ROWS,这将告诉MySQL提取行总数以及限制查询结果。

示例设置为:

SELECT SQL_CALC_FOUND_ROWS employeeName, phoneNumber FROM employee WHERE employeeName LIKE 'a%' LIMIT 10;

SELECT FOUND_ROWS();

在上述查询中,只需在其余所需查询中添加SQL_CALC_FOUND_ROWS选项并执行第二行,即SELECT FOUND_ROWS()返回该语句返回的结果集中的行数。

答案 4 :(得分:-9)

没有

理论上可能会获得一些小的收益,因为在引擎盖下有足够复杂的机器单独运行它们。但是,如果你想知道有多少行匹配一个条件,你必须计算它们而不仅仅是LIMITed子集。