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端編碼也很簡潔。
然而這種方式的缺點依然很明顯:我們不能對要返回的結果集做進一步的篩選(參考第一種選擇的優點二)。
----------------------------------------------最后分割線------------------------------------------------
總的來說,上述三種方法各有特點,我們在實際應用中應該秉着“合適的就是最好的”的原則來看待問題。
