如何从函数返回表的行类型和附加列?

Posted

技术标签:

【中文标题】如何从函数返回表的行类型和附加列?【英文标题】:How to return a table's rowtype plus an additional column from a function? 【发布时间】:2017-06-28 00:15:55 【问题描述】:

我有一个这样定义的表:

create table users (
  id serial primary key,
  name text,
  email text,
);

...我想编写一个返回形状行的函数:

(
  id integer,
  name text,
  email text,
  some_other_column boolean,
)

我设法使用下面的代码来解决这个问题,但我不想重新定义用户表中的列:

create or replace function get_users () 
returns table (
  id integer,
  name text,
  email text,
  some_other_column boolean,
) as $$
    select users.*, true as some_other_column from users;
$$ language sql;

有没有办法通过这样做来动态创建行类型? (postgres 在users.* 抱怨语法错误):

create or replace function get_users () 
returns table (
  users.*, 
  some_other_column boolean
) as $$
    select users.*, true as some_other_column from users;
$$ language sql;

请注意,以下直接执行的查询可以正常工作:

select users.*, true as some_other_column from users;

这里的最终目标是最终得到一个可调用的函数,如select * from get_users(),它返回的行包括现有表中的列和其他列。我不希望调用者担心究竟如何调用该函数。

我的假设是,由于我可以编写返回动态行的简单 sql,我应该能够以某种方式将该 sql 存储在数据库中,从而保留返回行的结构。

【问题讨论】:

【参考方案1】:

否。目前没有办法做到这一点(包括第 10 页)。

SQL 是一种严格类型的语言。创建函数时,必须声明返回类型。要返回一组行(您可以使用SELECT * FROM srf() 调用):

您可以返回匿名记录 (RETURNS SETOF record)。但是你必须在每次调用时提供一个列定义列表。

您可以返回多态(行)类型 (RETURNS SETOF anyelement)。但是您必须提供行类型 (composite type) 作为函数的参数,并且需要在系统中注册行类型不知何故

Refactor a PL/pgSQL function to return the output of various SELECT queries

您可以通过RETURNS SETOFrowtype 显式使用任何已注册的行类型。副作用是函数现在取决于行类型。

您可以使用 RETURNS TABLE (...) 临时定义返回的行类型 - 您甚至可以混合行类型 (composite types) 和简单类型。但是一个简单的SELECT * FROM srf() 不会分解嵌套的行类型——比如Mabu's answer 去演示。

相关:

Return SETOF rows from PostgreSQL function

这一切都归结为:

有没有办法通过这样做来动态创建行类型?

不,没有。 SELECT * FROM ... 将从系统目录中检索列定义列表,其中必须注册行类型之前您可以通过这种方式调用该函数。

通常最好在RETURNS TABLE () 子句中拼出列定义列表。这样可以避免依赖。如果您需要根据现有表快速注册行类型而不拼写其列,您可以创建一个VIEW - 或者如果它仅用于当前会话,则可以创建一个TEMPORARY VIEW

CREATE TEMP VIEW v_users_plus AS
SELECT *, NULL::boolean AS some_other_column FROM users;

这会在系统中注册一个同名的行类型 (v_users_plus),就像任何其他表或视图一样。对于非临时函数,显然需要非临时行类型。

【讨论】:

【参考方案2】:

您可以将表格视为伪类型,但您必须对函数和调用此函数的查询进行一些更改,如下所示。

创作:

create or replace function get_users () 
returns table (
  row_users users, 
  some_other_column boolean
) as $$
    select t, true as some_other_column from users as t;
$$ language sql;

调用:

SELECT (row_users).*, some_other_column FROM get_users();

事实上,你可以尝试其他动态结构的返回类型,例如Refcursor 或JSON ...这取决于你使用的语言或应用程序。

【讨论】:

这是一个很好的解决方法,但我的目标是让调用者只需要写select * from get_users()。数据库提供了一整套我希望保持一致的功能。也许有一种使用视图的方法? 我认为这行不通。当然,如果您在没有任何参数的情况下调用您的函数(与上面的示例相同),您可以创建一个视图为CREATE VIEW view_some_thing AS SELECT (row_users).*, some_other_column FROM get_users();。但是当您更新表用户的结构时,您必须重新创建视图。因为当你创建一个视图时,数据库会将视图的结构存储到系统表中,如果你改变结构,你也必须更新它。如果只是添加一些列,它不会引发错误,否则如果更改列名或列类型,数据库将引发异常。

以上是关于如何从函数返回表的行类型和附加列?的主要内容,如果未能解决你的问题,请参考以下文章

数据库SQL语句学习笔记-汇总数据

如何获取显示列异常值的行?

Oracle创建一个自定义函数,返回emp表的行数,请问哪位大神知道啊?

过滤后动态添加到表的行不隐藏

DAX从入门到精通 3-4-1 了解filter函数

SqlBulkCopy 无法尝试复制 XML 列中包含大量内容的行