可以設定一個游標來封裝查詢,然後一次讀取少量資料列的查詢結果,而不是一次執行整個查詢。這樣做的一個原因是避免結果包含大量資料列時記憶體溢出。(但是,PL/pgSQL 使用者通常不需要擔心這個問題,因為 FOR
迴圈會在內部自動使用游標來避免記憶體問題。)一個更有趣的用法是傳回函數建立的游標的參考,允許呼叫者讀取這些資料列。這提供了一種從函數傳回大型資料列集合的有效方式。
在 PL/pgSQL 中,對游標的所有存取都透過游標變數進行,游標變數的資料類型始終為特殊的 refcursor
。建立游標變數的一種方法是將其宣告為 refcursor
類型的變數。另一種方法是使用游標宣告語法,通常是
name
[ [ NO ] SCROLL ] CURSOR [ (arguments
) ] FORquery
;
(FOR
可以替換為 IS
以實現與 Oracle 的相容性。)如果指定了 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
被稱為未繫結,因為它未繫結到任何特定查詢。
當游標的查詢使用 FOR UPDATE/SHARE
時,無法使用 SCROLL
選項。此外,最好將 NO SCROLL
用於涉及 volatile 函數的查詢。SCROLL
的實作假設重新讀取查詢的輸出會產生一致的結果,而 volatile 函數可能不會這樣做。
在使用游標檢索資料列之前,必須先開啟它。(這相當於 SQL 指令 DECLARE CURSOR
。)PL/pgSQL 有三種形式的 OPEN
陳述式,其中兩種使用未繫結的游標變數,而第三種使用繫結的游標變數。
也可以透過 第 41.7.4 節 中描述的 FOR
陳述式,在不顯式開啟游標的情況下使用繫結的游標變數。FOR
迴圈將開啟游標,然後在迴圈完成時再次關閉它。
開啟游標涉及建立一個伺服器內部的資料結構,稱為入口,其中保存游標查詢的執行狀態。入口有一個名稱,在入口存在的期間內,該名稱在會話中必須是唯一的。依預設,PL/pgSQL 會為其建立的每個入口指派一個唯一的名稱。但是,如果您將非 null 字串值指派給游標變數,則該字串將用作其入口名稱。此功能可以使用 第 41.7.3.5 節 中所述的方式使用。
OPEN FOR
query
#OPENunbound_cursorvar
[ [ NO ] SCROLL ] FORquery
;
游標變數已開啟,並給定要執行的指定查詢。游標不能已開啟,並且必須宣告為未繫結的游標變數(也就是說,作為簡單的 refcursor
變數)。查詢必須是 SELECT
,或傳回資料列的其他內容(例如 EXPLAIN
)。查詢的處理方式與 PL/pgSQL 中的其他 SQL 指令相同: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
用來開啟一個游標變數,其查詢在宣告時就已經繫結到它。游標不能已經是開啟的狀態。如果且僅當游標被宣告為接受參數時,必須出現一個實際引數值表達式的清單。這些值將會被替換到查詢中。
繫結游標的查詢計畫總是會被認為是可以快取的;在這種情況下,沒有相當於 EXECUTE
的東西。請注意,SCROLL
和 NO SCROLL
不能在 OPEN
中指定,因為游標的捲動行為已經被決定。
引數值可以使用位置式或具名式的表示法傳遞。在位置式表示法中,所有引數都依照順序指定。在具名式表示法中,每個引數的名稱使用 :=
與引數表達式隔開。類似於呼叫函數,在第 4.3 節中描述的,也允許混合位置式和具名式表示法。
範例(這些使用上述的游標宣告範例)
OPEN curs2; OPEN curs3(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
值只是一個字串名稱,指向包含游標的有效查詢的入口。這個名稱可以傳遞、指派給其他的 refcursor
變數等等,而不會干擾該入口。)
所有的入口都會在交易結束時隱含地關閉。因此,refcursor
值只能在交易結束之前用來參考一個開啟的游標。
FETCH
#FETCH [direction
{ FROM | IN } ]cursor
INTOtarget
;
FETCH
從游標中(依指示的方向)擷取下一列到目標中,目標可以是一個列變數、一個紀錄變數,或是一個逗號分隔的簡單變數清單,就像 SELECT INTO
一樣。如果沒有適合的列,則目標會被設定為 NULL(s)。和 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
變數的名稱,該變數參考一個開啟的游標入口。
範例
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;
PL/pgSQL 函數可以將游標傳回給呼叫者。這對於傳回多列或多欄非常有用,尤其是在處理非常大的結果集時。為此,函數會開啟游標並將游標名稱傳回給呼叫者(或者只是使用呼叫者指定或以其他方式知道的入口名稱開啟游標)。然後,呼叫者可以從游標中擷取列。游標可以由呼叫者關閉,或者會在交易關閉時自動關閉。
游標所使用的入口名稱可以由程式設計師指定,或者自動產生。 若要指定入口名稱,只需在開啟游標之前,將字串賦予給 refcursor
變數。 refcursor
變數的字串值將會被 OPEN
用作底層入口的名稱。 然而,如果 refcursor
變數的值為空 (預設情況下會是空值),則 OPEN
會自動產生一個與任何現有入口不衝突的名稱,並將其賦予給 refcursor
變數。
在 PostgreSQL 16 之前的版本中,繫結游標變數會被初始化為包含它們自己的名稱,而不是保留為空值,因此預設情況下,底層入口名稱會與游標變數的名稱相同。 這樣的設計被更改的原因是,它在不同的函式中,造成了太多同名游標之間衝突的風險。
以下範例展示了呼叫者提供游標名稱的一種方式
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
期間一樣 (請參閱 Section 41.7.2.3)。
變數 recordvar
會自動定義為 record
類型,並且僅存在於迴圈內部 (迴圈內會忽略該變數名稱的任何現有定義)。 游標傳回的每一列都會依序賦予給這個紀錄變數,並且執行迴圈主體。
如果您在文件中發現任何不正確的地方,或者與您使用特定功能的經驗不符,或者需要進一步澄清,請使用此表單來回報文件問題。