與一次性執行整個查詢不同,可以設定一個封裝了該查詢的遊標,然後一次讀取幾行查詢結果。這樣做的一個原因是避免當結果包含大量行時記憶體溢位。(然而,PL/pgSQL 使用者通常不需要擔心這個問題,因為FOR
迴圈會自動在內部使用遊標來避免記憶體問題。)更有趣的用法是返回一個函式建立的遊標的引用,允許呼叫者讀取行。這提供了一種從函式中高效返回大型行集的方式。
PL/pgSQL 中對遊標的所有訪問都透過遊標變數進行,這些變數的型別始終是特殊的 refcursor
資料型別。建立遊標變數的一種方法是將其宣告為 refcursor
型別變數。另一種方法是使用遊標宣告語法,其一般形式是
name
[ [ NO ] SCROLL ] CURSOR [ (arguments
) ] FORquery
;
(為了與Oracle 相容,FOR
可以被替換為 IS
。) 如果指定了 SCROLL
,則遊標將能夠向後滾動;如果指定了 NO SCROLL
,則會拒絕向後取值;如果兩者都沒有出現,則是否允許向後取值取決於查詢。如果指定了arguments
,則它是成對的
組成的逗號分隔列表,這些列表定義了要在給定查詢中替換為引數值的名稱。在開啟遊標時,將指定這些名稱的實際值。name
datatype
一些例子
DECLARE curs1 refcursor; curs2 CURSOR FOR SELECT * FROM tenk1; curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key;
這三個變數的資料型別都是 refcursor
,但第一個可以用於任何查詢,第二個已經繫結了一個完全指定的查詢,而最後一個綁定了一個引數化查詢。(當遊標開啟時,key
將被替換為整數引數值。)變數 curs1
被稱為未繫結,因為它沒有繫結到任何特定的查詢。
SCROLL
選項不能與使用 FOR UPDATE/SHARE
的遊標查詢一起使用。另外,最好將 NO SCROLL
用於涉及易變函式的查詢。 SCROLL
的實現假定重新讀取查詢的輸出會得到一致的結果,而易變函式可能不會如此。
在使用遊標檢索行之前,必須先開啟它。(這是 SQL 命令 DECLARE CURSOR
的等效操作。)PL/pgSQL 有三種形式的 OPEN
語句,其中兩種使用未繫結的遊標變數,第三種使用繫結的遊標變數。
繫結遊標變數也可以透過 FOR
語句在不顯式開啟遊標的情況下使用,該語句在 第 41.7.4 節 中進行了描述。 FOR
迴圈會開啟遊標,並在迴圈完成後將其關閉。
開啟遊標涉及建立一個伺服器內部資料結構,稱為portal,它儲存遊標查詢的執行狀態。Portal 有一個名稱,在 portal 存在期間必須在會話中唯一。預設情況下,PL/pgSQL 會為其建立的每個 portal 分配一個唯一名稱。但是,如果將非空字串值賦給遊標變數,該字串將用作其 portal 名稱。此功能可以按 第 41.7.3.5 節 中描述的方式使用。
OPEN FOR
query
#OPENunbound_cursorvar
[ [ NO ] SCROLL ] FORquery
;
遊標變數被開啟並給定指定的查詢以執行。遊標不能已開啟,並且必須宣告為未繫結的遊標變數(即,作為一個簡單的 refcursor
變數)。查詢必須是 SELECT
,或任何其他返回行的內容(如 EXPLAIN
)。查詢的處理方式與其他 SQL 命令在 PL/pgSQL 中的處理方式相同:PL/pgSQL 變數名將被替換,並且查詢計劃將被快取以供將來重用。當 PL/pgSQL 變數被替換到遊標查詢中時,替換的值是 OPEN
時該變數的值;之後對該變數的更改不會影響遊標的行為。SCROLL
和 NO SCROLL
選項的含義與繫結遊標相同。
一個例子
OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;
OPEN FOR EXECUTE
#OPENunbound_cursorvar
[ [ NO ] SCROLL ] FOR EXECUTEquery_string
[ USINGexpression
[, ... ] ];
遊標變數被開啟並給定指定的查詢以執行。遊標不能已開啟,並且必須宣告為未繫結的遊標變數(即,作為一個簡單的 refcursor
變數)。查詢以字串表示式的形式指定,方式與 EXECUTE
命令相同。通常,這提供了靈活性,因此查詢計劃可以因執行而異(參見 第 41.11.2 節),而且還意味著命令字串中不會進行變數替換。與 EXECUTE
一樣,引數值可以透過 format()
和 USING
插入到動態命令中。SCROLL
和 NO SCROLL
選項的含義與繫結遊標相同。
一個例子
OPEN curs1 FOR EXECUTE format('SELECT * FROM %I WHERE col1 = $1',tabname) USING keyvalue;
在此示例中,表名透過 format()
插入到查詢中。col1
的比較值透過 USING
引數插入,因此不需要引用。
OPENbound_cursorvar
[ ( [argument_name
{ := | => } ]argument_value
[, ...] ) ];
此形式的 OPEN
用於開啟其查詢在宣告時已繫結到它的遊標變數。遊標不能已開啟。如果遊標宣告為接受引數,則必須提供實際引數值表示式列表。這些值將被替換到查詢中,就像在 OPEN
時一樣(參見 第 41.7.2.3 節)。
繫結遊標的查詢計劃始終被認為是可快取的;在這種情況下沒有 EXECUTE
的等價物。請注意,SCROLL
和 NO SCROLL
不能在 OPEN
中指定,因為遊標的滾動行為已經確定。
可以使用位置或命名表示法傳遞引數值。在位置表示法中,所有引數按順序指定。在命名錶示法中,使用 :=
或 =>
將每個引數的名稱與其引數表示式分隔開。類似於呼叫函式,如 第 4.3 節 中所述,也允許混合使用位置和命名錶示法。
示例(這些示例使用了上面的遊標宣告示例)
OPEN curs2; OPEN curs3(42); OPEN curs3(key := 42); OPEN curs3(key => 42);
由於繫結遊標的查詢會進行變數替換,因此實際上有兩種方法可以將值傳遞到遊標中:要麼透過對 OPEN
的顯式引數,要麼透過在查詢中引用 PL/pgSQL 變數來隱式傳遞。但是,只有在宣告繫結遊標之前宣告的變數才會被替換進去。在這兩種情況下,要傳遞的值都是在 OPEN
時確定的。例如,另一種獲得與上面 curs3
示例相同效果的方法是
DECLARE key integer; curs4 CURSOR FOR SELECT * FROM tenk1 WHERE unique1 = key; BEGIN key := 42; OPEN curs4;
開啟遊標後,可以使用此處描述的語句對其進行操作。
這些操作不必發生在開啟遊標的同一個函式中。您可以將 refcursor
值從函式返回給呼叫者,讓呼叫者操作該遊標。(在內部,refcursor
值只是包含遊標活動查詢的 portal 的字串名稱。此名稱可以傳遞,賦給其他 refcursor
變數,依此類推,而不會干擾 portal。)
所有 portal 在事務結束時都會被隱式關閉。因此,refcursor
值只能在事務結束前用於引用已開啟的遊標。
FETCH
#FETCH [direction
{ FROM | IN } ]cursor
INTOtarget
;
FETCH
將遊標中的下一行(按指定方向)檢索到目標中,目標可以是行變數、記錄變數或逗號分隔的簡單變數列表,就像 SELECT INTO
一樣。如果沒有合適的行,目標將被設定為 NULL。與 SELECT INTO
一樣,可以檢查特殊變數 FOUND
來檢視是否獲取了行。如果沒有獲取到行,則遊標將定位在最後一行之後或第一行之前,具體取決於移動方向。
direction
子句可以是 SQL FETCH 命令中允許的任何變體,但那些只能獲取一行的變體除外;即,它可以是 NEXT
、PRIOR
、FIRST
、LAST
、ABSOLUTE
count
、RELATIVE
count
、FORWARD
或 BACKWARD
。省略 direction
等同於指定 NEXT
。在帶有 count
的形式中,count
可以是任何整數值表示式(與僅允許整數常量的 SQL FETCH
命令不同)。需要向後移動的 direction
值很可能失敗,除非遊標在宣告或開啟時帶有 SCROLL
選項。
cursor
必須是一個 refcursor
變數的名稱,該變數引用了一個已開啟的遊標 portal。
示例
FETCH curs1 INTO rowvar; FETCH curs2 INTO foo, bar, baz; FETCH LAST FROM curs3 INTO x, y; FETCH RELATIVE -2 FROM curs4 INTO x;
MOVE
#MOVE [direction
{ FROM | IN } ]cursor
;
MOVE
在不檢索任何資料的情況下重新定位遊標。MOVE
的工作方式類似於 FETCH
命令,只是它只重新定位遊標而不返回移動到的行。direction
子句可以是 SQL FETCH 命令中允許的任何變體,包括可以獲取多行的變體;遊標將定位到最後一行。(然而,PL/pgSQL 中不推薦使用direction
子句僅為count
表示式且沒有關鍵字的情況。該語法與省略direction
子句的情況不明確,因此如果count
不是常量,則可能失敗。)與 SELECT INTO
一樣,可以檢查特殊變數 FOUND
來檢視是否有行可移動。如果沒有這樣的行,則遊標將定位在最後一行之後或第一行之前,具體取決於移動方向。
示例
MOVE curs1; MOVE LAST FROM curs3; MOVE RELATIVE -2 FROM curs4; MOVE FORWARD 2 FROM curs4;
UPDATE/DELETE WHERE CURRENT OF
#UPDATEtable
SET ... WHERE CURRENT OFcursor
; DELETE FROMtable
WHERE CURRENT OFcursor
;
當遊標定位在錶行上時,可以使用該遊標來更新或刪除該行。遊標的查詢有(特別是,沒有分組)限制,最好在遊標中使用 FOR UPDATE
。有關更多資訊,請參閱 DECLARE 參考頁。
一個例子
UPDATE foo SET dataval = myval WHERE CURRENT OF curs1;
CLOSE
#CLOSE cursor
;
CLOSE
關閉已開啟遊標的底層 portal。這可以用於在事務結束前釋放資源,或使遊標變數可以重新開啟。
一個例子
CLOSE curs1;
PL/pgSQL 函式可以向呼叫者返回遊標。這對於返回多行或多列非常有用,尤其是對於非常大的結果集。為此,函式會開啟遊標並將其遊標名稱返回給呼叫者(或者只是使用呼叫者指定或呼叫者已知的 portal 名稱開啟遊標)。呼叫者然後可以從遊標中獲取行。遊標可以由呼叫者關閉,或者在事務結束時自動關閉。
遊標使用的 portal 名稱可以由程式設計師指定,也可以自動生成。要指定 portal 名稱,只需在開啟遊標之前將字串賦給 refcursor
變數。 OPEN
將 refcursor
變數的字串值用作底層 portal 的名稱。但是,如果 refcursor
變數的值為 null(預設情況下就是這樣),則 OPEN
會自動生成一個與任何現有 portal 都不衝突的名稱,並將其賦給 refcursor
變數。
在 PostgreSQL 16 之前,繫結遊標變數被初始化為包含它們自己的名稱,而不是留空,因此底層 portal 名稱預設與遊標變數的名稱相同。此更改是因為它增加了不同函式中相似命名的遊標之間發生衝突的風險。
以下示例展示了一種呼叫者可以提供遊標名稱的方式
CREATE TABLE test (col text); INSERT INTO test VALUES ('123'); CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS ' BEGIN OPEN $1 FOR SELECT col FROM test; RETURN $1; END; ' LANGUAGE plpgsql; BEGIN; SELECT reffunc('funccursor'); FETCH ALL IN funccursor; COMMIT;
以下示例展示了使用自動遊標名稱生成
CREATE FUNCTION reffunc2() RETURNS refcursor AS ' DECLARE ref refcursor; BEGIN OPEN ref FOR SELECT col FROM test; RETURN ref; END; ' LANGUAGE plpgsql; -- need to be in a transaction to use cursors. BEGIN; SELECT reffunc2(); reffunc2 -------------------- <unnamed cursor 1> (1 row) FETCH ALL IN "<unnamed cursor 1>"; COMMIT;
以下示例展示了一種從單個函式返回多個遊標的方式
CREATE FUNCTION myfunc(refcursor, refcursor) RETURNS SETOF refcursor AS $$ BEGIN OPEN $1 FOR SELECT * FROM table_1; RETURN NEXT $1; OPEN $2 FOR SELECT * FROM table_2; RETURN NEXT $2; END; $$ LANGUAGE plpgsql; -- need to be in a transaction to use cursors. BEGIN; SELECT * FROM myfunc('a', 'b'); FETCH ALL FROM a; FETCH ALL FROM b; COMMIT;
有一個 FOR
語句的變體,它允許迭代遊標返回的行。語法是
[ <<label
>> ] FORrecordvar
INbound_cursorvar
[ ( [argument_name
{ := | => } ]argument_value
[, ...] ) ] LOOPstatements
END LOOP [label
];
遊標變數在宣告時必須已繫結到某個查詢,並且它不能已開啟。FOR
語句會自動開啟遊標,並在迴圈退出時再次關閉遊標。如果遊標宣告為接受引數,則必須提供實際引數值表示式列表。這些值將像在 OPEN
時一樣(參見 第 41.7.2.3 節)被替換到查詢中。
變數 recordvar
會自動定義為 record
型別,並且只在迴圈內部存在(在迴圈內部,任何已有的變數名定義都會被忽略)。遊標返回的每一行都會依次賦給這個記錄變數,然後執行迴圈體。
如果您在文件中發現任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用 此表單 報告文件問題。