1. 程式人生 > >【轉載】postgresql儲存過程中返回型別

【轉載】postgresql儲存過程中返回型別

前言

假如需要在pg上面寫儲存過程,那麼返回型別必定是一個疑問點。
一般而言,對於一個查詢語句,為了可以統一動態返回各種資料,我們是不會蠢到每一個儲存過程都額外編寫一個返回型別 譬如:
(id,name,password)
之類的,所以在寫的時候肯定會考慮 setof record這個動態返回結果集,可惜,這是pg的一個坑,具體請看:

儲存過程的結果集返回大概型別

參考如下:
postgresql 函式返回結果集

postgresql 函式返回結果集
2011年11月26日 22:56:07 oemoon 閱讀數:12269 標籤: postgresql function jdbc security table 語言  更多
個人分類: 資料庫
pgsql function 系列之一:返回結果集
--------------------------------------------------------------------------------
我們在編寫postgresql資料庫的函式(或稱為儲存過程)時,時常會遇到需要返回一個結果集的情況,如何返回一個結果集,返回一個結果集有多少種方式,以及如何選擇一個合適的方式返回結果集,這是一個需要仔細考慮的問題。本文僅簡單的羅列出各種返回結果集的方式並試圖分析他們的特點,而採用何種方式則留給大家自己去判斷。 
閱讀本文需要一定的postgresql資料庫端程式設計知識,如果你缺乏這方面的知識,請先翻閱postgresql文件。 

----------------------------------------------------分割線------------------------------------------------------- 

第一種選擇:宣告setof 某表/某檢視 返回型別 
這是postgresql官方推薦的返回結果集的形式,當你查閱postgresql官方文件的時候,你會看到的就是這種形式。如果採用這種形式,你的function程式碼看起來會像這樣:CREATE OR REPLACE FUNCTION function1 () RETURNS setof table1 AS 
$body$ 
DECLARE 
result record; 
BEGIN 
for result in select * from table1 loop 
return next result; 
end loop; 
return; 
END; 
$body$ 
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER; 

這是使用pl/pgsql語言的風格,你甚至可以使用sql語言讓程式碼看起來更簡單: 

CREATE OR REPLACE FUNCTION function1 () RETURNS SETOF table1 AS 
$body$ 
SELECT * from table1; 
$body$ 
LANGUAGE 'sql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER; 

以下是分析: 
首先我們說優點,第一、這是官方推薦的;第二、當使用pl/pgsql語言的時候,我們可以在迴圈中加上判斷語句,僅返回我們需要的行;第三、在jdbc呼叫中,我們可以像查詢普通的表一樣使用這個function,例如:"select * from function1()"。 
其次我們說缺點,第一、當使用pl/pgsql語言的時候,即使我們需要返回所有行,仍然要進行迴圈導致不必要的開銷。當然你可以使用sql語言避免這個問題,但顯然sql語言的控制能力太弱以至於我們無法使用它實現哪怕稍微複雜一點的邏輯。第二、返回的欄位必須是在function定義時就確定了的,這意味著我們無法動態的返回我們想要返回的欄位。 

------------------------------------------------繼續分割線------------------------------------------------- 

第二種選擇:宣告setof record返回型別 
總的說起來這種方式和上一種沒什麼不同,它唯一的用處在於“一定程度上”解決了動態返回結果集欄位的問題。之所以說“一定程度”是因為使用setof record返回型別將會導致極度噁心的jdbc端編碼——你不得不在sql語句後面顯式的說明所選欄位的名稱和型別,並且你會發現你無法使用jdbc call的方式呼叫function(關於jdbc call的部分在另外的篇幅描述)。具體的來說,如果你返回一個結果集欄位按照:a bigint,b varchar,c timestamp排列,則你的jdbc端編碼看起來會像這樣: 

select * from function1() as (a bigint,b varchar,c timestamp); 

問題在於,如果你不知道function將要返回的欄位型別是什麼,則你根本無法在jdbc端呼叫該function!!! 

----------------------------------------------還是分割線------------------------------------------------- 

第三種選擇:宣告refcursor返回型別 
事情到這裡將揭過新的一頁,這裡我們放棄使用setof ××× ,而使用一個全新的返回型別——refcursor(遊標)。關於什麼是遊標本文不再累述,請自己翻閱相關文件,這裡僅描述如何使用。 
首先,要使用遊標,你的function編碼看起來會像這樣: 
CREATE OR REPLACE FUNCTION function1 () RETURNS refcursor AS 
$body$ 
DECLARE 
result refcursor; 
BEGIN 
open result for select * from table1,table2; --你可以任意選擇你想要返回的表和欄位 
return result; 
END; 
$body$ 
LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER; 

你的jdbc端編碼推薦使用call方式,看起來會像這樣: 

Connection conn = ConnectionPool.getConn(); 
CallableStatement proc = conn.prepareCall("{ ? = call function1() }"); 
proc.execute(); 
ResultSet rs = (ResultSet) proc.getObject(1); 

如此我們就獲得了一個結果集,並且該結果集所包含的欄位可以是任意你想要的欄位,並且jdbc端編碼也很簡潔。 

然而這種方式的缺點依然很明顯:我們不能對要返回的結果集做進一步的篩選(參考第一種選擇的優點二)。 
----------------------------------------------最後分割線------------------------------------------------ 

總的來說,上述三種方法各有特點,我們在實際應用中應該秉著“合適的就是最好的”的原則來看待問題。

儲存過程返回遊標時候的demo例子

postgreSQL中function返回結果集

postgresql對於各種遊標的使用示例

CREATE OR REPLACE FUNCTION cursor_demo()
  RETURNS refcursor AS
$BODY$
declare
	unbound_refcursor refcursor;
	v_id int;
	v_step_desc varchar(1000);
begin
	open unbound_refcursor for execute 'select id,step_desc from t_runtime_step_log';
	loop
		fetch unbound_refcursor into v_id,v_step_desc;
		
		if found then
			raise notice '%-%',v_id,v_step_desc;
		else
			exit;
		end if;
	end loop;
	close unbound_refcursor;
	raise notice 'the end of msg...';
	return unbound_refcursor;
exception when others then
	raise exception 'error--(%)',sqlerrm;
end;
$BODY$
  LANGUAGE plpgsql;
 
CREATE OR REPLACE FUNCTION cursor_demo1(refcursor)
  RETURNS refcursor AS
$$
begin
	open $1 for select * from t_runtime_step_log;
	return $1;
exception when others then
	raise exception 'sql exception--%',sqlerrm;
end;
$$
  LANGUAGE plpgsql;
 
begin;
select cursor_demo1('a');
fetch all in a;
--commit;
 
CREATE OR REPLACE FUNCTION cursor_demo2()
  RETURNS refcursor AS
$$
declare
  bound_cursor cursor for select * from t_runtime_step_log;
begin
  open bound_cursor;
  return bound_cursor;
end;
$$
  LANGUAGE plpgsql;
 
begin;
select cursor_demo2();
fetch all in bound_cursor;
--commit;
 
CREATE OR REPLACE FUNCTION cursor_demo3(p_condition integer)
  RETURNS refcursor AS
$BODY$
declare
	bound_param_cursor cursor(id_condition integer) for select * from t_runtime_step_log where id > id_condition;
begin
	open bound_param_cursor(p_condition);
	return bound_param_cursor;
end;
$BODY$
  LANGUAGE plpgsql;
 
begin;
select cursor_demo3(5);
fetch all in bound_param_cursor;
--commit;
 
 
 CREATE OR REPLACE FUNCTION cursor_demo4(p_condition integer)
  RETURNS refcursor AS
$$
declare
	bound_param_cursor cursor for select * from t_runtime_step_log where id > p_condition;
begin
	open bound_param_cursor;
	return bound_param_cursor;
end;
$$
  LANGUAGE plpgsql;
 
begin;
select cursor_demo4(5);
fetch all in bound_param_cursor;
--commit;
--------------------- 
作者:jsjw18 
來源:CSDN 
原文:https://blog.csdn.net/victor_ww/article/details/44240063 
版權宣告:本文為博主原創文章,轉載請附上博文連結!

postgresql資料庫的遊標使用例子說明

部分內容參考了http://blog.csdn.net/victor_ww/article/details/44240063

但是由於上面的文章,在我那裡不能完全執行,所以我這邊整理了一份可以執行成功的例子。

有的時候,我們會使用到函式,並且想使用函式中查詢一些資料,並且當是多條結果集的時候,就需要使用遊標了。

使用遊標分為兩種方式,這個也是自己參考上面的文章,然後自己瞎摸索出來的,多試幾次。

1、沒有where 條件的
CREATE OR REPLACE FUNCTION cursor_demo()  
  RETURNS refcursor AS  
$BODY$  
declare  
    unbound_refcursor refcursor;  
    v_id int;  
    v_step_desc varchar(1000);  
begin  
    open unbound_refcursor for execute 'select id,step_desc from t_runtime_step_log';  
    loop  
        fetch unbound_refcursor into v_id,v_step_desc;  
          
        if found then  
            raise notice '%-%',v_id,v_step_desc;  
        else  
            exit;  
        end if;  
    end loop;  
    close unbound_refcursor;  
    raise notice 'the end of msg...';  
    return unbound_refcursor;  
exception when others then  
    raise exception 'error--(%)',sqlerrm;  
end;  
$BODY$  
  LANGUAGE plpgsql;  

上面只是聲明瞭一個函式,外部呼叫需要採用下面的方式。
begin;  
select cursor_demo();  
commit;
呼叫時,如果不使用begin和commit的話,會提示錯誤。

當然也可以再封裝一個函式, 該函式中寫上上面的程式碼。

unnamed portal

2、附帶where條件的
CREATE OR REPLACE FUNCTION cursor_demo3(p_condition integer)  
  RETURNS refcursor AS  
$BODY$  
declare  
    bound_param_cursor cursor(id_condition integer) for select * from t_runtime_step_log where id > id_condition;  
begin  
    open bound_param_cursor(p_condition);  
    return bound_param_cursor;  
end;  
$BODY$  
  LANGUAGE plpgsql;  

上面只是聲明瞭一個函式,外部呼叫需要採用下面的方式。

begin;  
select cursor_demo();  
commit;
呼叫時,如果不使用begin和commit的話,會提示錯誤。

當然也可以再封裝一個函式, 該函式中寫上上面的程式碼。

unnamed portal
--------------------- 
作者:跨時代135 
來源:CSDN 
原文:https://blog.csdn.net/baidu_18607183/article/details/56012327 
版權宣告:本文為博主原創文章,轉載請附上博文連結!

如何直接在在sql語句中呼叫遊標並且得到結果集

好了,你會發現根據上面寫的返回的結果是:

在這裡插入圖片描述

所以,還差一步:

postgres直接檢視function返回的遊標資料

在一些複雜的返回cursor的function中,經常需要檢視返回的cursor的具體資料,已驗證自己是否讀懂了原先的sql,或者驗證自己書寫的是否正確,如果啟動一個呼叫程式就比較麻煩了,pgAdmin可以是直接檢視。

pgAdmin支援在sql視窗直接解析遊標資料,就是直接檢視遊標的具體資料,不過需要特殊的查詢sql,具體如下:

1、首先需要開啟事務:執行begin;語句。

2、執行select function_name();語句。此語句會返回一個類似於<unnamed portal 18>的東西。

3、檢視遊標代表的具體資料:執行語句:fetch all from "<unnamed portal 18>";(雙引號不能少)

此語句中雙引號的裡面是第二步返回的資料,返回值就是此遊標代表的資料。

4、最後記得把此事務commit或者rollback了。
--------------------- 
作者:suiyue_cxg 
來源:CSDN 
原文:https://blog.csdn.net/suiyue_cxg/article/details/54429392 
版權宣告:本文為博主原創文章,轉載請附上博文連結!

於是就有了:


begin;
select func04();
fetch all from "<unnamed portal 1>";
commit;

好,試試結果:

在這裡插入圖片描述

在這裡插入圖片描述

好了,遊標的用法以及大體呼叫方式已經整理得差不多了。

如何在儲存過程中呼叫其他儲存過程返回的遊標且得到結果

我們先來看看舊版本的做法:

postgresql 8.0.0中文文件的 35.8. 遊標介紹

PL/pgSQL 函式可以向呼叫者返回遊標。 這個功能用於從函式裡返回多行或多列。要想這麼做的時候, 該函式開啟遊標並且把該遊標的名字返回給呼叫者。 呼叫者然後從遊標裡FETCH行。 遊標可以由呼叫者關閉,或者是在事務結束的時候自動關閉。 

函式返回的遊標名可以由呼叫者宣告或者自動生成。 要宣告一個信使的名字,只要再開啟遊標之前,給 refcursor 變數賦予一個字串就可以了。 refcursor 變數的字串值將被 OPEN 當作下層的信使的名字使用。 不過,如果 refcursor 變數是空,那麼 OPEN 將自動生成一個和現有信使不衝突的名字, 然後將它賦予 refcursor 變數。 

注意: 一個繫結的遊標變數其名字初始化為對應的字串值,因此信使的名字和遊標變數名同名, 除非程式設計師再開啟遊標之前通過賦值覆蓋了這個名字。但是一個未繫結的遊標變數初始化的時候預設是空, 因此它會收到一個自動生成的唯一的名字,除非被覆蓋。 

下面的例子顯示了一個呼叫者宣告遊標名字的方法: 

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;

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;
    RETURN;
END;
$$ LANGUAGE plpgsql;

-- 需要在事務裡使用遊標。
BEGIN;

SELECT * FROM myfunc('a', 'b');

FETCH ALL FROM a;
FETCH ALL FROM b;
COMMIT;

不過你會發現,新版本(我的是pg10)已經沒辦法這樣用了,例如這個例子:

create or replace function func05(varchar(50))
returns refcursor as

$$
BEGIN


open $1 for select * from member;

return $1;


END;
$$LANGUAGE plpgsql volatile ;


在這裡插入圖片描述

好了,直接找到文件來看看,是9.4的文件:

Cursors

40.7. Cursors
Rather than executing a whole query at once, it is possible to set up a cursor that encapsulates the query, and then read the query result a few rows at a time. One reason for doing this is to avoid memory overrun when the result contains a large number of rows. (However, PL/pgSQL users do not normally need to worry about that, since FOR loops automatically use a cursor internally to avoid memory problems.) A more interesting usage is to return a reference to a cursor that a function has created, allowing the caller to read the rows. This provides an efficient way to return large row sets from functions.

40.7.1. Declaring Cursor Variables
All access to cursors in PL/pgSQL goes through cursor variables, which are always of the special data type refcursor. One way to create a cursor variable is just to declare it as a variable of type refcursor. Another way is to use the cursor declaration syntax, which in general is:

name [ [ NO ] SCROLL ] CURSOR [ ( arguments ) ] FOR query;
(FOR can be replaced by IS for Oracle compatibility.) If SCROLL is specified, the cursor will be capable of scrolling backward; if NO SCROLL is specified, backward fetches will be rejected; if neither specification appears, it is query-dependent whether backward fetches will be allowed. arguments, if specified, is a comma-separated list of pairs name datatype that define names to be replaced by parameter values in the given query. The actual values to substitute for these names will be specified later, when the cursor is opened.

Some examples:

DECLARE
    curs1 refcursor;
    curs2 CURSOR FOR SELECT * FROM tenk1;
    curs3 CURSOR (key integer) FOR SELECT * FROM tenk1 WHERE unique1 = key;
All three of these variables have the data type refcursor, but the first can be used with any query, while the second has a fully specified query already bound to it, and the last has a parameterized query bound to it. (key will be replaced by an integer parameter value when the cursor is opened.) The variable curs1 is said to be unbound since it is not bound to any particular query.

40.7.2. Opening Cursors
Before a cursor can be used to retrieve rows, it must be opened. (This is the equivalent action to the SQL command DECLARE CURSOR.) PL/pgSQL has three forms of the OPEN statement, two of which use unbound cursor variables while the third uses a bound cursor variable.

Note: Bound cursor variables can also be used without explicitly opening the cursor, via the FOR statement described in Section 40.7.4.

40.7.2.1. OPEN FOR query
OPEN unbound_cursorvar [ [ NO ] SCROLL ] FOR query;
The cursor variable is opened and given the specified query to execute. The cursor cannot be open already, and it must have been declared as an unbound cursor variable (that is, as a simple refcursor variable). The query must be a SELECT, or something else that returns rows (such as EXPLAIN). The query is treated in the same way as other SQL commands in PL/pgSQL: PL/pgSQL variable names are substituted, and the query plan is cached for possible reuse. When a PL/pgSQL variable is substituted into the cursor query, the value that is substituted is the one it has at the time of the OPEN; subsequent changes to the variable will not affect the cursor's behavior. The SCROLL and NO SCROLL options have the same meanings as for a bound cursor.

An example:

OPEN curs1 FOR SELECT * FROM foo WHERE key = mykey;
40.7.2.2. OPEN FOR EXECUTE
OPEN unbound_cursorvar [ [ NO ] SCROLL ] FOR EXECUTE query_string
                                     [ USING expression [, ... ] ];
The cursor variable is opened and given the specified query to execute. The cursor cannot be open already, and it must have been declared as an unbound cursor variable (that is, as a simple refcursor variable). The query is specified as a string expression, in the same way as in the EXECUTE command. As usual, this gives flexibility so the query plan can vary from one run to the next (see Section 40.10.2), and it also means that variable substitution is not done on the command string. As with EXECUTE, parameter values can be inserted into the dynamic command via USING. The SCROLL and NO SCROLL options have the same meanings as for a bound cursor.

An example:

OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident(tabname)
                                        || ' WHERE col1 = $1' USING keyvalue;
In this example, the table name is inserted into the query textually, so use of quote_ident() is recommended to guard against SQL injection. The comparison value for col1 is inserted via a USING parameter, so it needs no quoting.

40.7.2.3. Opening a Bound Cursor
OPEN bound_cursorvar [ ( [ argument_name := ] argument_value [, ...] ) ];
This form of OPEN is used to open a cursor variable whose query was bound to it when it was declared. The cursor cannot be open already. A list of actual argument value expressions must appear if and only if the cursor was declared to take arguments. These values will be substituted in the query.

The query plan for a bound cursor is always considered cacheable; there is no equivalent of EXECUTE in this case. Notice that SCROLL and NO SCROLL cannot be specified in OPEN, as the cursor's scrolling behavior was already determined.

Argument values can be passed using either positional or named notation. In positional notation, all arguments are specified in order. In named notation, each argument's name is specified using := to separate it from the argument expression. Similar to calling functions, described in Section 4.3, it is also allowed to mix positional and named notation.

Examples (these use the cursor declaration examples above):

OPEN curs2;
OPEN curs3(42);
OPEN curs3(key := 42);
Because variable substitution is done on a bound cursor's query, there are really two ways to pass values into the cursor: either with an explicit argument to OPEN, or implicitly by referencing a PL/pgSQL variable in the query. However, only variables declared before the bound cursor was declared will be substituted into it. In either case the value to be passed is determined at the time of the OPEN. For example, another way to get the same effect as the curs3 example above is

DECLARE
    key integer;
    curs4 CURSOR FOR SELECT * FROM tenk1 WHERE unique1 = key;
BEGIN
    key := 42;
    OPEN curs4;
40.7.3. Using Cursors
Once a cursor has been opened, it can be manipulated with the statements described here.

These manipulations need not occur in the same function that opened the cursor to begin with. You can return a refcursor value out of a function and let the caller operate on the cursor. (Internally, a refcursor value is simply the string name of a so-called portal containing the active query for the cursor. This name can be passed around, assigned to other refcursor variables, and so on, without disturbing the portal.)

All portals are implicitly closed at transaction end. Therefore a refcursor value is usable to reference an open cursor only until the end of the transaction.

40.7.3.1. FETCH
FETCH [ direction { FROM | IN } ] cursor INTO target;
FETCH retrieves the next row from the cursor into a target, which might be a row variable, a record variable, or a comma-separated list of simple variables, just like SELECT INTO. If there is no next row, the target is set to NULL(s). As with SELECT INTO, the special variable FOUND can be checked to see whether a row was obtained or not.

The direction clause can be any of the variants allowed in the SQL FETCH command except the ones that can fetch more than one row; namely, it can be NEXT, PRIOR, FIRST, LAST, ABSOLUTE count, RELATIVE count, FORWARD, or BACKWARD. Omitting direction is the same as specifying NEXT. In the forms using a count, the count can be any integer-valued expression (unlike the SQL FETCH command, which only allows an integer constant). direction values that require moving backward are likely to fail unless the cursor was declared or opened with the SCROLL option.

cursor must be the name of a refcursor variable that references an open cursor portal.

Examples:

FETCH curs1 INTO rowvar;
FETCH curs2 INTO foo, bar, baz;
FETCH LAST FROM curs3 INTO x, y;
FETCH RELATIVE -2 FROM curs4 INTO x;
40.7.3.2. MOVE
MOVE [ direction { FROM | IN } ] cursor;
MOVE repositions a cursor without retrieving any data. MOVE works exactly like the FETCH command, except it only repositions the cursor and does not return the row moved to. As with SELECT INTO, the special variable FOUND can be checked to see whether there was a next row to move to.

Examples:

MOVE curs1;
MOVE LAST FROM curs3;
MOVE RELATIVE -2 FROM curs4;
MOVE FORWARD 2 FROM curs4;
40.7.3.3. UPDATE/DELETE WHERE CURRENT OF
UPDATE table SET ... WHERE CURRENT OF cursor;
DELETE FROM table WHERE CURRENT OF cursor;
When a cursor is positioned on a table row, that row can be updated or deleted using the cursor to identify the row. There are restrictions on what the cursor's query can be (in particular, no grouping) and it's best to use FOR UPDATE in the cursor. For more information see the DECLARE reference page.

An example:

UPDATE foo SET dataval = myval WHERE CURRENT OF curs1;
40.7.3.4. CLOSE
CLOSE cursor;
CLOSE closes the portal underlying an open cursor. This can be used to release resources earlier than end of transaction, or to free up the cursor variable to be opened again.

An example:

CLOSE curs1;
40.7.3.5. Returning Cursors
PL/pgSQL functions can return cursors to the caller. This is useful to return multiple rows or columns, especially with very large result sets. To do this, the function opens the cursor and returns the cursor name to the caller (or simply opens the cursor using a portal name specified by or otherwise known to the caller). The caller can then fetch rows from the cursor. The cursor can be closed by the caller, or it will be closed automatically when the transaction closes.

The portal name used for a cursor can be specified by the programmer or automatically generated. To specify a portal name, simply assign a string to the refcursor variable before opening it. The string value of the refcursor variable will be used by OPEN as the name of the underlying portal. However, if the refcursor variable is null, OPEN automatically generates a name that does not conflict with any existing portal, and assigns it to the refcursor variable.

Note: A bound cursor variable is initialized to the string value representing its name, so that the portal name is the same as the cursor variable name, unless the programmer overrides it by assignment before opening the cursor. But an unbound cursor variable defaults to the null value initially, so it will receive an automatically-generated unique name, unless overridden.

The following example shows one way a cursor name can be supplied by the caller:

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;
The following example uses automatic cursor name generation:

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;
The following example shows one way to return multiple cursors from a single function:

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;
40.7.4. Looping Through a Cursor's Result
There is a variant of the FOR statement that allows iterating through the rows returned by a cursor. The syntax is:

[ <<label>> ]
FOR recordvar IN bound_cursorvar [ ( [ argument_name := ] argument_value [, ...] ) ] LOOP
    statements
END LOOP [ label ];
The cursor variable must have been bound to some query when it was declared, and it cannot be open already. The FOR statement automatically opens the cursor, and it closes the cursor again when the loop exits. A list of actual argument value expressions must appear if and only if the cursor was declared to take arguments. These values will be substituted in the query, in just the same way as during an OPEN (see Section 40.7.2.3).

The variable recordvar is automatically defined as type record and exists only inside the loop (any existing definition of the variable name is ignored within the loop). Each row returned by the cursor is successively assigned to this record variable and the loop body is executed.


注意到,共有兩種方式可以呼叫遊標:

在這裡插入圖片描述

好了,下面改改例子:

create or replace function func05(caller_cursor refcursor)
returns refcursor as

$$
BEGIN


open caller_cursor for select * from member;

return caller_cursor;


END;
$$LANGUAGE plpgsql volatile ;


接下來,執行:

在這裡插入圖片描述

在這裡插入圖片描述

注意,字串中是不分大小寫的!

返回複合結果的處理方式

下面以一個例子說明情況:
對於一個分頁的儲存過程,要求能夠返回總記錄數量,總共多少頁,還有要返回記錄結果集,如下:
程式碼如下:


CREATE OR REPLACE FUNCTION public.test_pager(	
	in pageIndex integer,
	in pageSize integer,
	records_cursor refcursor,
	OUT total_size integer,
	OUT total_pages integer
	)
  RETURNS refcursor
/*
* 該函式用於試驗如何一次返回多個結果。
*/
    
AS $BODY$
declare tmpVal vachar(200);
begin
open records_cursor for select * from member;
total_size:=2;
total_pages:=1;
tmpVal:='hello world';
RETURN records_cursor;
end;
$BODY$
LANGUAGE plpgsql volatile;

執行結果如下:
在這裡插入圖片描述

在多個結果集時候,返回的result type 必須為record。。

這。。。壓根沒法用。因為儲存過程的一個很重要的優點是,應用伺服器(譬如java)不需要多次讀取資料庫的資料,只需要一次取就是了,所以,必須要有自由返回任意結果或者結果集的機制。

參考資料之後,改寫為 setof refcursor:


CREATE OR REPLACE FUNCTION public.test_pager(	
	in pageIndex integer,
	in pageSize integer,
	outline_cursor refcursor,
	records_cursor refcursor
	)
  RETURNS setof refcursor
/*
* 該函式用於試驗如何一次返回多個結果。
*/
    
AS $BODY$
declare tmpVal varchar(200);
declare total_size integer;
declare total_pages integer;
begin
open records_cursor for select * from member;
total_size:=2;
total_pages:=1;
tmpVal:='hello world';

/*返回多個結果集*/
open outline_cursor for select total_size , total_pages;
return next outline_cursor;
RETURN next records_cursor;
end;
$BODY$
LANGUAGE plpgsql volatile;

執行程式碼:

begin;
select test_pager(1, 1, 'outline_cursor', 'record_cursor');
fetch all in outline_cursor;
fetch all in record_cursor;
end;

執行結果:

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

基本上算是完美解決分頁儲存過程返回多個結果集的難點了。

返回多個結果集的處理形式

jdbc呼叫儲存過程獲取多個結果集

Spring JdbcTemplate方法詳解