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端编码也很简洁。
然而这种方式的缺点依然很明显:我们不能对要返回的结果集做进一步的筛选(参考第一种选择的优点二)。
----------------------------------------------最后分割线------------------------------------------------
总的来说,上述三种方法各有特点,我们在实际应用中应该秉着“合适的就是最好的”的原则来看待问题。
--------------------------------------------------------------------------------
我们在编写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端编码也很简洁。
然而这种方式的缺点依然很明显:我们不能对要返回的结果集做进一步的筛选(参考第一种选择的优点二)。
----------------------------------------------最后分割线------------------------------------------------
总的来说,上述三种方法各有特点,我们在实际应用中应该秉着“合适的就是最好的”的原则来看待问题。