扩展现有 PostgreSQL 类型

Extend existing PostgreSQL type

提问人:Nicholas Chiang 提问时间:9/6/2021 最后编辑:Erwin BrandstetterNicholas Chiang 更新时间:9/6/2021 访问量:901

问:

是否可以在 PostgreSQL 中扩展现有数据类型?从本质上讲,我想要这个 TypeScript 的等效项,但在 SQL 中:

interface Meeting {
  id: number;
  start: Date;
  end: Date;
  description: string;
}

interface ServiceHour extends Meeting {
  total: number;
  hours: number;
}

因为我有一个函数,它返回表中的所有列,然后返回在查询时计算的另外两列和列。下面是该函数的样子:meetingstotalhours

create or replace function user_hours(org_id text, user_id text)
returns table (like meeting_instances)
as $$
select (sum(hours) over (order by _.instance_time)) total, * from (
  select
    extract(epoch from ((meeting_instances.time).to - (meeting_instances.time).from)) / 60 / 60 as hours, 
    meeting_instances.*
  from meeting_instances inner join relation_people on relation_people.meeting = meeting_instances.id
  where relation_people.user = user_id
  and meeting_instances.org = org_id
  and meeting_instances.instance_time <= current_date
) as _;
$$
language sql stable;

现在,我收到一个类型不匹配错误,因为与包含列和两个新列的表不同。我希望能够做的是这样的事情(显然下面的语法实际上并不存在......但我不确定是否有另一种方法可以使用类似的速记语法来做到这一点):table (like meeting_instances)meeting_instanceshourstotal

returns table (total float, hours float, meeting_instances.*)
returns table (total float, hours float) intersect (like meeting_instances)
returns table (total float, hours float) extends (like meeting_instances)

我目前的解决方法是创建一个视图,然后让该函数简单地查询该视图并返回视图的类型。

SQL PostgreSQL 创建函数 复合类型

评论

1赞 Erwin Brandstetter 9/6/2021
首先披露您的 Postgres 版本和所涉及的 Postgres 表的实际定义 ()。似乎您有多个嵌套复合类型层。您可以在 Postgres 中执行此操作。并不意味着你应该这样做。CREATE TABLE ...
0赞 Bergi 9/6/2021
@ErwinBrandstetter 您可以嵌套它们,但可以扩展它们(例如表定义上的子句)吗?LIKE
0赞 Erwin Brandstetter 9/6/2021
@Bergi:不,你不能。不在条款中,不在其他地方。实际上甚至没有记录在案的语法。我正在解开答案中的多层......RETURNSRETURNS (LIKE tbl)
0赞 Bergi 9/6/2021
@ErwinBrandstetter 是的,我就是这么想的。但我不明白为什么 OP 似乎有“多层嵌套复合类型”?期待您的回答:-)
0赞 Erwin Brandstetter 9/6/2021
meeting_instances.time已经是嵌套复合类型。我们不知道兔子洞有多深。

答:

7赞 Erwin Brandstetter 9/6/2021 #1

对于您的核心问题

是否可以扩展现有数据类型?

,不可能。不在 的子句中,在 PostgreSQL 中的其他任何地方都不行,直到版本 14。RETURNSCREATE FUNCTION

您可以返回复合类型的字段,以及任何类型的其他字段。但这有微妙的不同:

CREATE FUNCTION user_hours_plus( ...)
  RETURNS TABLE (my_meeting meeting_instances, hours numeric, total numeric) ...

使用以下命令调用该函数:

SELECT * FROM user_hours_plus('a', 'b');

返回嵌套复合类型作为返回列之一,例如:

(1,"2017-01-03","2017-01-04", foo) | 123 | 345

若要分解复合类型,可以使用以下命令进行调用:

SELECT (my_meeting).*, hours, total FROM user_hours_plus('a', 'b');

但我不会去那里。

我目前的解决方法是创建一个视图,然后让该函数简单地查询该视图并返回视图的类型。

然后只需使用视图。不要在其上创建其他函数。案件已结案。

如果你真的想说:

...然后在函数的 RETURNS 子句中使用视图的行类型

然后我们回到你的问题。CREATE VIEW 以(隐式)注册扩展行类型是一个有效的选项 - 特别是因为它是 had 情况的方便语法简写。但对于初学者来说,SELECT *

RETURNS TABLE (LIKE meeting_instances)

...未记录 CREATE FUNCTION 的语法。没有人应该使用它。可能会在下一个版本之一中删除,恕不另行通知。
规范的、等效的、记录的语法是:

RETURNS SETOF meeting_instances

(与 some_table 一样)CREATE TABLE 的语法记录。CREATE FUNCTIONRETURNS 子句目前支持相同的内容,但未记录在案,因此请勿使用它。

返回到创建 .如果除了注册该扩展行类型之外没有使用 ,请考虑改用 CREATE TYPE。不幸的是,也不允许语法。您必须拼出复合类型的所有列(属性)。喜欢:VIEWVIEWCREATE TYPELIKE other_type

CREATE TYPE meeting_plus AS (
   id numeric
 , start date
 , "end" date
 , description text
 , total numeric
 , hours numeric
);

然后,您可以使用:

RETURNS SETOF meeting_plus

就像你想要的一样。

但是对于一个函数,我会改用并拼写出返回类型:RETURNS TABLE()

RETURNS TABLE (
   id numeric
 , start date
 , "end" date
 , description text
 , total numeric
 , hours numeric)

哦,我不会在Postgres中使用“start”和“end”作为标识符。两者都是标准 SQL 中的保留字。“end”在 Postgres 中是绝对保留的,并且必须始终用双引号引起来。