从PostgreSQL函数返回SETOF行

时间:2013-07-25 17:32:31

标签: postgresql plpgsql dynamic-sql postgresql-9.2

我有一种情况,我希望在两个视图之间返回连接。那是很多专栏。在sql server中很容易。但是在PostgreSQL中我加入时。我收到错误“需要列定义列表”。

有什么方法可以绕过这个,我不想提供返回列的定义。

CREATE OR REPLACE FUNCTION functionA(username character varying DEFAULT ''::character varying, databaseobject character varying DEFAULT ''::character varying)
  RETURNS SETOF ???? AS
$BODY$
Declare 
SqlString varchar(4000) = '';
BEGIN
IF(UserName = '*') THEN
   Begin
   SqlString  := 'select * from view1 left join ' + databaseobject  + ' as view2 on view1.id = view2.id';
   End;
ELSE
    Begin
    SqlString := 'select * from view3 left join ' + databaseobject  + ' as view2 on view3.id = view2.id';
    End;
END IF; 
execute (SqlString  );
END;
$BODY$

1 个答案:

答案 0 :(得分:27)

消毒功能

The manual具有PL / pgSQL的所有基础知识。基本上,你所拥有的东西可以简化/消毒:

CREATE OR REPLACE FUNCTION func_a(username text = '', databaseobject text = '')
  RETURNS ???? AS
$func$
BEGIN

RETURN QUERY EXECUTE
format ('SELECT * FROM %s v1 LEFT JOIN %I v2 USING (id)'
       , CASE WHEN username = '*' THEN 'view1' ELSE 'view3' END, databaseobject);

END
$func$  LANGUAGE plpgsql;
  • 您不需要BEGIN .. END in the function body的其他实例,除非启动一个具有自己范围的单独代码块,这是很少需要的。

  • 标准SQL并置运算符为||+是您以前的供应商的“创意”添加。

  • 除非您引用它们,否则请勿使用CaMeL-case identifiers。最好不要使用它们:

  • varchar(4000)也适用于SQL Server的特定限制。这种数据类型在Postgres中没有任何性能优势。如果您确实需要4000个字符的限制,请仅使用它。我只想使用text - 除了在我简化函数之后我们不需要任何变量

  • 如果您尚未使用format()consult the manual here

返回类型

现在,对于您的实际问题:动态查询的返回类型有点棘手,因为SQL要求函数返回定义良好的类型。如果数据库中的表或视图或复合类型已经与要返回的列定义列表匹配,则可以使用它:

CREATE FUNCTION foo()
  RETURNS SETOF my_view AS
...

如果您正在进行输入,则可以返回匿名记录:

CREATE FUNCTION foo()
  RETURNS SETOF record AS
...

或提供带有(最简单)RETURNS TABLE的列定义列表:

CREATE FUNCTION foo()
  RETURNS TABLE (col1 int, col2 text, ...) AS
...

匿名记录的缺点:然后你必须为每次调用提供一个列定义列表,所以我几乎不会使用它。

我不会开始使用SELECT *。使用确定的列列表返回并相应地声明您的返回类型:

CREATE OR REPLACE FUNCTION func_a(username text = '', databaseobject text = '')
  RETURNS TABLE(col1 int, col2 text, col3 date) AS
$func$
BEGIN

RETURN QUERY EXECUTE
format ('SELECT v1.col1, v1.col2, v2.col3
         FROM %s v1 LEFT JOIN %I v2 USING (id)$f$
       , CASE WHEN username = '*' THEN 'view1' ELSE 'view3' END, databaseobject);

END
$func$;

对于完全动态的查询,我宁愿使用纯SQL查询开头。不是功能。

还有更多高级选项,但您可能需要先学习基础知识。