返回带有附加列的表的函数

bca*_*tle 6 postgresql datatypes view functions set-returning-functions

我想编写一个 PostgreSQL 函数,它返回一个表和一个附加列。有没有办法在不手动指定的情况下做到这一点RETURNS TABLE (col1 type, col2 type, ...)

例如,考虑以下函数:

CREATE FUNCTION get_users_with_most_videos_since_time(ts TIMESTAMPTZ)
  RETURNS SETOF "user" AS $$
    SELECT
      u.*,
      count(v.id) AS vids_since

    FROM "user" AS u
    INNER JOIN "video" AS v ON v.creator_id = u.id
    WHERE v.created_at > ts

    GROUP BY u.id
    ORDER BY vids_since DESC;
$$ LANGUAGE SQL;
Run Code Online (Sandbox Code Playgroud)

这失败并出现错误:

ERROR:  return type mismatch in function declared to return "user"
DETAIL:  Final statement returns too many columns.
Run Code Online (Sandbox Code Playgroud)

公平地说,我们包括了该vids_since列,该列不存在于"user"表中。

因此,要解决此问题,我想将其更改为:

CREATE FUNCTION get_users_with_most_videos_since_time(ts TIMESTAMPTZ)
  RETURNS 
    TABLE (<<< all columns from table "user" >>>, vids_since BIGINT) 
  AS $$
    ...
Run Code Online (Sandbox Code Playgroud)

有没有办法做到这一点而不必在"user"此处重新复制表的整个架构?

Erw*_*ter 3

AIUI,您的愿望是缩短RETURNS函数的子句。不确定您是否想同时建立对表的行类型的依赖关系,但这在这里也是有意义的。

该表单RETURNS SETOFrettype依赖于存储在系统目录中的使用类型。手册:

返回类型可以是基类型、复合类型或域类型,也可以引用表列的类型。

因此,只需在系统中注册您想要的行类型(一次)。您可以使用 显式创建复合类型CREATE TYPE。或者您可以通过创建另一个表、视图或物化视图来隐式地执行此操作。即使只是临时函数的临时视图或表(在会话结束时死亡)。就像@Abelisto 评论的那样:

CREATE VIEW user_plus AS 
SELECT *, null::bigint AS vids_since
FROM   "user"
WHERE  false;
Run Code Online (Sandbox Code Playgroud)

但是,被解析为视图创建时的SELECT *列列表(“早期绑定”)。如果您稍后向基础表添加列,则视图及其行类型不会更新,并且在执行函数时会出现另一种类型不匹配的情况。但是,尝试从基础表中删除列抱怨视图中依赖于它的列。你必须改变观点和功能。

顺便说一句,您的功能可以简化且更快:

CREATE FUNCTION get_users_with_most_videos_since_time(_ts timestamptz)
  RETURNS SETOF user_plus AS
$func$
   SELECT *   -- effectively the same as: u.*, v.vids_since
   FROM   "user" AS u
   JOIN  (    -- aggregate *before* you join
      SELECT creator_id AS id, count(*) AS vids_since  -- note the column alias
      FROM   video v
      WHERE  created_at > _ts
      GROUP  BY 1
      ) v USING (id)  -- USING only retains one id column
   ORDER  BY v.vids_since DESC;
$func$  LANGUAGE SQL;
Run Code Online (Sandbox Code Playgroud)

拥有这个功能实际上是有意义的。您想要按结果中未包含的列进行过滤。普通的VIEW.

旁白:我不鼓励使用像user这样的保留字作为标识符。那是一把上了膛的步枪。

  • 谢谢,这真的很有帮助。还要感谢@Abelisto。顺便说一句,欧文你在这里和上的答案非常有帮助,特别是关于[多态类型](http://stackoverflow.com/a/11751557/1161906)的答案。感谢您抽出宝贵的时间。 (3认同)