运行 带有 LIMIT/OFFSET 的查询,同时获取总行数

Run a query with a LIMIT/OFFSET and also get the total number of rows

出于分页目的,我需要一个 运行 带有 LIMITOFFSET 子句的查询。但是我还需要计算在没有 LIMITOFFSET 子句的情况下该查询将返回的行数。

我要运行:

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

并且:

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

同时。有没有办法做到这一点,特别是让 Postgres 对其进行优化的方法,以便它比单独 运行ning 更快?

没有

理论上,您可以通过足够复杂的机器在引擎盖下单独获得 运行 它们的一些小收益。但是,如果您想知道有多少行符合某个条件,您必须对它们进行计数,而不仅仅是有限的子集。

是。用一个简单的window函数:

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

请注意,成本将大大高于没有总数的成本,但通常仍比两个单独的查询便宜。 Postgres 实际上必须 计算所有行 两种方式,这会根据符合条件的行总数强加成本。详情:

  • Best way to get result count before LIMIT was applied

然而,当 OFFSET 至少与 return 从基本查询编辑的行数一样大时,没有行被 returned。所以我们也没有得到 full_count.

如果这是不可接受的,一个可能的 解决方法总是 return 完整计数 将使用 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;

如果 OFFSET 太大,您会得到一行 NULL 值并附加 full_count。否则,它会像第一个查询一样附加到每一行。

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

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

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

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

's answer is perfect if you need an accurate value. However, on large tables you often only need a pretty good approximation. Postgres gives you just that 它会更快,因为它不需要评估每一行:

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 外部化或将其作为标准查询是否有优势。它值得一些测试。

SELECT t.*, pgc.reltuples AS total_count
FROM tbl as t
RIGHT JOIN pg_class pgc ON pgc.relname = 'tbl'
WHERE /* something */
ORDER BY /* something */
OFFSET ?
LIMIT ?

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

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

您可能需要考虑使用 returns 总计数 仅一次 的方法,如下所示:

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
     WHERE /* sth */
    ) as count, 
    (SELECT json_agg(t.*) FROM (
        SELECT * FROM table
        WHERE /* sth */
        ORDER BY col1
        OFFSET ?
        LIMIT ?
    ) AS t) AS rows