运行 带有 LIMIT/OFFSET 的查询,同时获取总行数
Run a query with a LIMIT/OFFSET and also get the total number of rows
出于分页目的,我需要一个 运行 带有 LIMIT
和 OFFSET
子句的查询。但是我还需要计算在没有 LIMIT
和 OFFSET
子句的情况下该查询将返回的行数。
我要运行:
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
出于分页目的,我需要一个 运行 带有 LIMIT
和 OFFSET
子句的查询。但是我还需要计算在没有 LIMIT
和 OFFSET
子句的情况下该查询将返回的行数。
我要运行:
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 时有效。我会放它以防它可以帮助某人,但它可能无法完全回答最初的问题。
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