出于分页目的,我需要使用LIMIT
和OFFSET
子句运行查询。但是我还需要计算那个没有LIMIT
和OFFSET
子句的查询返回的行数。
我想跑:
SELECT * FROM table WHERE /* whatever */ ORDER BY col1 LIMIT ? OFFSET ?
和
SELECT COUNT(*) FROM table WHERE /* whatever */
同时。有没有办法做到这一点,特别是让Postgres优化它的方式,这样它比单独运行更快?
答案 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子集。