如何从函数返回表的行类型和附加列?
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) 作为函数的参数,并且需要在系统中注册行类型不知何故。
您可以通过RETURNS SETOF
rowtype
显式使用任何已注册的行类型。副作用是函数现在取决于行类型。
您可以使用 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();
。但是当您更新表用户的结构时,您必须重新创建视图。因为当你创建一个视图时,数据库会将视图的结构存储到系统表中,如果你改变结构,你也必须更新它。如果只是添加一些列,它不会引发错误,否则如果更改列名或列类型,数据库将引发异常。以上是关于如何从函数返回表的行类型和附加列?的主要内容,如果未能解决你的问题,请参考以下文章