回答

收藏

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

技术问答 技术问答 60 人阅读 | 0 人回复 | 2023-09-13

出于分页目的,我需要使用LIMIT和OFFSET子句运行查询。但是我还需要计算不带LIMITandOFFSET子句的查询将返回的行数。
- O5 c2 w0 @5 W9 |( q, k我要跑步:0 y+ L3 t3 s' _& F0 w/ g9 H1 k
SELECT * FROM table WHERE /* whatever */ ORDER BY col1 LIMIT ? OFFSET ?! |- P0 l* i/ S9 I- S, x4 }
和:9 \/ h% ?# h9 n! k
SELECT COUNT(*) FROM table WHERE /* whatever */& V2 U' \1 p5 Z  Z/ S3 P5 G0 k
同时。有没有办法做到这一点,特别是让Postgres优化它的方法,使其比单独运行它们都快?
" `  t9 G1 L+ ?8 d6 f9 m! c                # n/ F8 y( K4 G% i& y4 K
解决方案:
- q. C9 n: T6 C                * f6 v0 \% {' ^$ G+ {+ G- B

- L8 o& ]7 c5 v8 N" U4 l3 ^) n+ F: A2 o- V
                是的。 具有简单的窗口功能:9 \0 ~' q1 Q- p6 c' x' b
SELECT *, **count(*) OVER() AS full_count**
( y# Q1 r) r$ {: UFROM   tbl
% k! F6 J9 X1 z/ |3 ^2 V9 h! l' kWHERE  /* whatever *// Z0 ]4 u9 n/ |1 k
ORDER  BY col1
) Z" X- i  n, I1 c  r; B+ SOFFSET ?
3 O5 r) Y: S7 H) C: d; q" {LIMIT  ?
) T" o" }5 K+ V9 E$ m, ^请注意,成本将比没有总数大得多,但通常仍比两个单独的查询便宜。Postgres必须实际上以任何一种方式对 所有行 进行 计数, b  E' @: ~( w: I) N0 {
,这取决于符合条件的行的总数而产生了费用。' F5 H  l" f5 t6 x9 M) ~( n
但是 ,正如Dani所指出的那样,当OFFSET至少等于从基本查询返回的行数时,将不返回任何行。所以我们也没有得到full_count。
7 B% G  ]+ b4 K7 U  R& N( ]8 |* r" n如果不可接受,则 始终返回全部计数 的可能 解决方法 是CTE和OUTER JOIN:
$ h- O; T- D/ o7 ^WITH cte AS (
% S$ L* |: q; N9 |0 }$ e& @   SELECT *
# ~, b( z' S3 d2 c' S! ]& i6 F. I7 Y   FROM   tbl
* f: A, U+ V! t) L. b   WHERE  /* whatever */4 _& h3 J" B' V: a* ~( Q3 f9 R
   )% B* Z6 b! l" {: P. o
SELECT *
7 A4 d. K& z/ n3 z. j7 ?8 ^6 z+ H  WFROM  (! `8 S# u$ f4 L4 E+ r  X. @
   TABLE  cte
5 C8 V9 y/ V9 w7 n   ORDER  BY col1. k1 j0 s4 a  U2 b) j1 S% X( x" G/ C! i
   LIMIT  ?
* [8 y- r$ K8 W3 G# o6 `0 L; x   OFFSET ?
  o% b  F) `: a* p) M   ) sub
: H8 @4 o0 p" `+ ~8 w& c0 A+ D. PRIGHT  JOIN (SELECT count(*) FROM cte) c(full_count) ON true;5 e" s) y) o; r' x# O; B5 l
如果行太大,full_count则会得到一行NULL值,并附加OFFSET。否则,它会像第一个查询一样附加到每一行。
- t7 p* ~: L4 H* d, c! {' z/ H如果具有所有NULL值的行是可能的有效结果,则必须检查offset >= full_count以消除空行的来源的歧义。
/ K/ W5 M0 b8 ^这仍然只执行一次基本查询。但这会增加查询的开销,并且只有在比重复进行基本查询的次数少的情况下才需要付费。
  ~% n% p* B7 }/ G' B如果支持最终排序顺序的索引可用,则可能需要将其包括ORDER BY在CTE中(冗余)。
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则