本文介绍了DECLARE的语法、参数以及示例等内容。
简介
DECLARE
允许用户创建游标, 游标可以被用来在大型查询暂停时检索少量的行。游标被创建后, 可以用 FETCH 从中取得行。
语法
DECLARE name [ BINARY ] [ INSENSITIVE ] [ [ NO ] SCROLL ]
CURSOR [ { WITH | WITHOUT } HOLD ] FOR query
参数
name
要创建的游标的名称。BINARY
让游标返回二进制数据而不是返回文本格式数据。INSENSITIVE
指示从游标中检索数据的过程不受游标创建之后在其底层表上发生的更新的影响。在PolarDB中,这是默认的行为。因此这个关键词没有实际效果,仅仅被用于兼容 SQL 标准。SCROLL
NO SCROLL
SCROLL
指定游标可以用非顺序(例如,反向) 的方式从中检索行。根据查询的执行计划的复杂度,指定SCROLL
可能导致查询执行时间上的性能损失。NO SCROLL
指定游标不能以非顺序的方式从中检索行。默认是允许在某些情况下滚动,但这和指定SCROLL
不完全相同。WITH HOLD
WITHOUT HOLD
WITH HOLD
指定该游标在创建它的事务提交之后还能被继续使用。WITHOUT HOLD
指定该游标不能在创建它的事务之外使用。如果两者都没有指定,则默认为WITHOUT HOLD
。query
用于提供该游标返回的行的 SELECT 或者 VALUES 命令。关键词
BINARY
、INSENSITIVE
和SCROLL
可以以任意顺序出现。
说明
普通游标以文本格式返回数据,这和SELECT
产生的数据一样。 BINARY
选项指定游标应该以二进制格式返回数据。这减少了服务器和客户端的转换负担,但程序员需要付出更多工作来处理与平台相关的二进制数据格式。例如,如果一个查询从一个整数列中返回一个值一,用一个默认游标将得到一个字符串1
,而使用一个二进制游标将得到该值的四字节内部表示(big-endian 大端字节顺序)。
使用二进制游标时应该小心。很多应用(包括 psql)还没有准备好处理二进制游标, 它们仍然期待数据以文本格式到来。
当客户端应用使用“扩展查询”协议发出一个 FETCH
命令,绑定协议消息会指定使用文本还是二进制格式检索数据。这种选择会覆盖定义游标时指定的方式。因此,在使用扩展查询协议时,这样一个二进制游标的概念实际是被废弃的 — 任何游标都可以被视作文本或者二进制。
除非指定了WITH HOLD
,这个命令创建的游标只能在当前事务中使用。因此,没有WITH HOLD
的DECLARE
在事务块外是没有用的:游标只会生存到该语句结束。因此如果这种命令在事务块之外被使用, PolarDB会报告一个错误。 定义事务块需要使用 BEGIN 和 COMMIT (或者 ROLLBACK)。
如果指定了WITH HOLD
并且创建游标的事务成功提交,在同一个会话中的后续事务中还能够继续访问该游标( 但是如果创建事务被中止,游标会被移除)。一个用 WITH HOLD
创建的游标可以用一个显式的 CLOSE
命令关闭,或者会话结束时它也会被关闭。在当前的实现中,由一个被保持游标表示的行会被复制到一个临时文件或者内存区域中,这样它们才会在后续事务中保持可用。
当查询包括FOR UPDATE
或FOR SHARE
时, 不能指定WITH HOLD
在定义一个将被反向取元组的游标时,应该指定SCROLL
选项。这是 SQL 标准所要求的。不过,为了和早期版本兼容, 如果游标的查询计划足够简单到支持它不需要额外的开销, PolarDB会允许在没有 SCROLL
的情况下反向取元组。不过,建议应用开发者不要依赖于从没有用SCROLL
创建的游标中反向取元组。如果指定了NO SCROLL
,那么任何情况下都不允许反向取元组。
当查询包括FOR UPDATE
或FOR SHARE
时, 也不允许反向取元组。因此在这种情况下不能指定 SCROLL
。
如果可滚动和WITH HOLD
游标调用了任何不稳定的函数,它们可能给出预期之外的结果。当重新取得一个之前取得过的行时,那些函数会被重新执行,这可能导致得到与第一次不同的结果。对这类情况的一种变通方法是,声明游标为WITH HOLD
并且在从其中读取任何行之前提交事务。这将强制该游标的整个输出被物化在临时存储中,这样针对每一行只会执行一次不稳定函数。
如果游标的查询包括FOR UPDATE
或者FOR SHARE
,那么被返回的行会在它们第一次被取得时被锁定,这和带有这些选项的常规 SELECT 命令一样。此外,被返回的行将是最新的版本,因此这些选项提供了被 SQL 标准称为 “敏感游标”的等效体(把INSENSITIVE
与 FOR UPDATE
或者FOR SHARE
一起指定是错误)。
如果游标要和UPDATE ... WHERE CURRENT OF
或者 DELETE ... WHERE CURRENT OF
一起使用,通常推荐使用FOR UPDATE
。使用FOR UPDATE
可以阻止其他会话在行被取得和被更新之间修改行。如果没有 FOR UPDATE
,当行在游标创建后被更改后,一个后续的 WHERE CURRENT OF
命令将不会产生效果。
另一个使用FOR UPDATE
的原因是,如果没有它,当游标查询不符合 SQL 标准的“简单可更新”规则时,后续的 WHERE CURRENT OF
可能会失败(特别地,该游标必须只引用一个表并且没有使用分组或者ORDER BY
)。不是简单可更新的游标可能成功也可能不成功,这取决于计划选择的细节。因此在最坏的情况下,应用可能会在测试时成功但是在生产中失败。如果指定了FOR UPDATE
, 则保证游标是可更新的。
不把FOR UPDATE
和WHERE CURRENT OF
一起用的主要原因是,需要游标时可滚动的或者对于后续更新不敏感(也就是说,继续显示旧的数据)。如果这是你的需求,应密切关注按上述警示。
SQL 标准只对嵌入式 SQL 中的游标做出了规定。 PolarDB服务器没有为游标实现 OPEN
语句。当游标被声明时就被认为已经被打开。不过,ECPG( PolarDB的嵌入式 SQL 预处理器) 支持标准 SQL 游标习惯,包括那些DECLARE
和OPEN
语句。
你可以通过查询 pg_cursors 系统视图可以看到所有可用的游标。
示例
声明一个游标:
DECLARE liahona CURSOR FOR SELECT * FROM films;