游标和提取在 PostgreSQL 中如何工作

How does cursor and fetch work in PostgreSQL

我想知道 CURSORFETCHPostgreSQL 中是如何工作的。

一开始我以为

  1. 当用select语句声明CURSOR时,DB将执行select语句,然后将结果存储在DB内存中。

  2. 当在 CURSOR 上调用 FETCH 时,DB 将只读取在 CURSOR 上移动的结果。

  3. CURSOR 关闭时,存储的结果将从内存中删除。

如果我的假设是正确的,无论 select 语句多么复杂,FETCH 都应该有较短的响应时间。

但是,当我测试时,FETCH 显示的响应时间比我预期的要差,就像它做了一些我没有预料到的事情。

它们是如何工作的?

------------编辑--------

下面是我用实际数据库测试时得到的结果 table。 (select 语句包含 3 table 的 join 子句,其中一个 table 有 300 万行)

(   8sec) DECLARE “123" NO SCROLL CURSOR WITH HOLD FOR SELECT .....
(0.04sec) FETCH FORWARD 2 FROM "123";
(   4sec) FETCH FORWARD 10000 FROM "123";

------------编辑--------

FETCH FORWARD 10000 FROM "123" 中的 4 秒响应时间似乎是因为我使用了 pgcli(PostgreSQL 客户端工具)。

我不知道为什么,但在更改客户端工具后,它显然已经快到了 0.04 秒。

SQL Commands: DECLARE:

In the current implementation, the rows represented by a held cursor are copied into a temporary file or memory area so that they remain available for subsequent transactions.

这取决于您是使用单个事务中的游标还是使用 "WITH HOLD" 和多个事务。

如果您使用 "WITH HOLD",则在调用 "DECLARE" 的事务的 "COMMIT" 上,将使用游标中的所有数据创建临时 table。如果数据量很大,table 会保存到磁盘,因此获取速度会稍微慢一些。但并没有那么慢,因为这应该是对一些合理数量的行的顺序扫描。

tometzky=> begin;
BEGIN
Time: 0.301 ms
tometzky=> declare c no scroll cursor with hold for select pg_sleep(1) from generate_series(1,6);
DECLARE CURSOR
Time: 1.140 ms
tometzky=> commit;
COMMIT
Time: 6007.180 ms (00:06.007)
tometzky=> fetch forward 3 from c;
 pg_sleep 
----------



(3 rows)

Time: 0.384 ms
tometzky=> fetch forward 3 from c;
 pg_sleep 
----------



(3 rows)

Time: 0.336 ms
tometzky=> fetch forward 3 from c;
 pg_sleep 
----------
(0 rows)

Time: 0.338 ms

当您使用来自调用 DECLARE 的同一事务的游标时,一旦请求的行数可用,每个 FETCH 将 return:

tometzky=> begin;
BEGIN
Time: 0.301 ms
tometzky=> declare c no scroll cursor for select pg_sleep(1) from generate_series(1,6);
DECLARE CURSOR
Time: 1.225 ms
tometzky=> fetch forward 3 from c;
 pg_sleep 
----------



(3 rows)

Time: 3004.041 ms (00:03.004)
tometzky=> fetch forward 3 from c;
 pg_sleep 
----------



(3 rows)

Time: 3003.855 ms (00:03.004)
tometzky=> fetch forward 3 from c;
 pg_sleep 
----------
(0 rows)

Time: 0.229 ms
tometzky=> commit;
COMMIT
Time: 0.444 ms

但是,例如,如果您使用的查询需要在最后一步进行排序,则它必须先获取所有行才能对它们进行排序。