PostgreSQL:如何在不使用“列定义列表”的情况下从表中返回动态行?

时间:2014-04-04 11:09:03

标签: postgresql plpgsql polymorphic-associations

如何在不使用“列定义列表”的情况下动态检索表中的行?

我试图通过使用多态类型“anyelement”(伪类型)来做同样的但是得到错误“查询结构与函数结果类型不匹配”。

例如:我有一个名为“table1”的表,其中包含以下详细信息。

- 表

create table table1
( 
  slno integer,
  fname varchar,
  lname varchar,
  city varchar,
  country varchar
)

- 功能

create or replace function function1(column_name varchar,relation_name anyelement)
returns setof anyelement as
$fun$
declare 
       cname varchar; 
       add_column varchar;
       group_column varchar;
       select_query varchar;
begin
       if column_name='fname' then
          cname:=quote_ident(column_name);
          add_column:='"city"'||','||'"country"';
          group_column:='"slno"'||','||cname||','||'"city"'||','||'"country"';

       else 
          cname:=quote_ident(column_name);
          add_column:='"city"'||','||'"country"';
          group_column:='"slno"'||','||cname||','||'"city"'||','||'"country"';

       end if;

       select_query:='select slno,'||cname||','||add_column||' from '||pg_typeof(relation_name) || 'group by '||group_column;

       return query execute select_query;
end;
$fun$
language plpgsql;

---函数调用

select * from function1('fname',NULL::table1);

2 个答案:

答案 0 :(得分:4)

anyelement中描述了relation_name作为返回类型的处理:

  

当函数的返回值被声明为多态类型时,   必须至少有一个也是多态的参数位置,   并且作为参数提供的实际数据类型确定了   该调用的实际结果类型。

在您的情况下,此参数的anyelement类型为NULL::table1,并且通过传递function1,这确实告诉规划人员SETOF table1的此特定调用应返回{{ 1}}。到目前为止一切都很好。

现在的问题是,一旦执行,该函数不会返回SETOF table1,而是返回其他内容。这不是执行者所期待的,因此也就是错误。

尽管问题的标题是如何返回动态行... ,但您似乎想要的是动态列或多态结果集。

这对SQL来说是一场艰苦的战斗,因为为了构建查询的执行计划,计划程序必须知道每个列的每个中间结果的类型。如果您使用必须执行的函数设计查询以查找其输出结构,则会产生鸡和蛋的问题:计划必须先于执行,而不能依赖于它。

由于其动态类型推断技术应用于anyelement,PostgreSQL已经在推动这个约束实现尽可能多的多态性。

答案 1 :(得分:2)

这是因为如果您使用NULL::table1的{​​{1}}值调用您的函数,则必须返回relation_name

  

多态参数和结果相互关联,并在解析调用多态函数的查询时解析为特定的数据类型。声明为anyelement的每个位置(参数或返回值)都允许具有任何特定的实际数据类型,但在任​​何给定的调用中,它们必须都是相同的实际类型。

     

http://www.postgresql.org/docs/9.3/static/extend-type-system.html#EXTEND-TYPES-POLYMORPHIC

但你想带着

返回
SETOF table1

不是(slno integer, fname varchar, city varchar, country varchar) 的行(错过table1 - 第3列)。

如果您愿意仅使用lname varchar'fname'来调用此功能,那么您的功能可以更加简单:

'lname'

通过这种方式,您可以使用create or replace function function1( column_name varchar, relation_name anyelement ) returns table ( slno integer, name varchar, city varchar, country varchar ) language plpgsql as $fun$ begin return query execute format( $sql$ select slno, %1$I AS name, city, country from %2$I group by slno, %1$I, city, country $sql$, column_name, pg_typeof(relation_name) ); end; $fun$; NULL::table1调用您的函数,但是如果您愿意,也可以使用varchar作为relation_name(这将更具可读性,例如relation_name 1}}参数)。