将列添加到%rowtype的记录变量

时间:2017-03-23 02:22:02

标签: sql postgresql function types plpgsql

我在PostgreSQL 9.2.9中有一个需要返回多列的plpgsql函数。大多数列来自单个表,所以我已经声明了虚拟表(该术语是否正确?)rc为:

rc "Sequence"%rowtype;

这很有效,除了我还有2列要添加到返回的数据。如果我用以下内容替换上述声明,该函数可以正常工作:

 rc RECORD;

以及其他一些小代码更改。但是,我需要始终使用包含所有列定义的非常长SELECT命令来明确调用该函数。

如何充分利用这两个方面,即返回"Sequence"表格的所有列,并添加2个新列("AverageSED""avDailySED"),其中包含以下内容:

SELECT * FROM production1('2016-02-27 00:00:00','2016-03-11 00:00:00');

这是我的一般功能,作为使用RECORD类型的示例(为了便于阅读而减少)。

CREATE OR REPLACE FUNCTION production1(tme1 timestamp without time zone, tme2 timestamp without time zone, mn integer)
  RETURNS SETOF record AS
$BODY$
DECLARE
 -- Could possibly use (CREATE TYPE rcholder %rowtype) and use a function with a sub-function that then takes the rc RECORD and recasts it to type %rowtype. This would mean the column descriptor in the SELECT call could be dropped
  rc record;
  AverageSED Real;
  avDailySED Real;

BEGIN
-- Calculate average of value from Log_Alpha table
SELECT AVG("logSED")::Real
    FROM "Log_Alpha"
    WHERE "logTime" >= tme1 AND "logTime" < tme2
    INTO averageSED;

-- Select the required row from the sequence data table
  FOR rc IN
    SELECT *, AverageSED, avDailySED
    FROM "Sequence"
    WHERE "Sequence"."seqMinute" = mn AND "Sequence"."seqTime" >= tmeA AND "Sequence"."seqTime" <= tmeB
    ORDER BY "Sequence"."seqTime"
  LOOP
    rc."seqTime" = rc."seqTime" - '1 day'::interval;

  -- Use a subquery to calculate the average SED for all data for the day pertaining to this record date field
    SELECT AVG("logSED")::Real into rc.avDailySED FROM "Log_Alpha"
    WHERE "Log_Alpha"."logTime" >= rc."seqTime" and "Log_Alpha"."logTime" < (rc."seqTime" + '1 day'::interval);

    RETURN NEXT rc ;-- (AverageSED,avDailySED);
  END LOOP;
END
$BODY$
  LANGUAGE plpgsql;

因此,由于SELECT表中可用的所有列,此函数需要超过1400个字符的非常长的"Sequence"查询。

2 个答案:

答案 0 :(得分:1)

你的问题是函数的返回类型, 函数中的变量rc,它可以正常工作。

您可以在RETURNS SETOF <row_type>like @Pavel demonstrates中创建另一种类型。 (但是不要在这里使用继承,这是一个学术的问题,就像帕维尔已经提到的那样。)

您可以使用RETURNS TABLE ()代替。 (这就是我要做的。)

此外,您的整个函数可以从根本上简化为具有LATERAL子查询的单个SQL查询(需要 Postgres 9.3 + ,请考虑升级到当前版本!):

CREATE OR REPLACE FUNCTION production1(tme1 timestamp, tme2 timestamp, mn int)
  RETURNS TABLE ( -- list all columns of "Sequence" here! Plus ...
                , average_sed  real
                , av_daily_sed real) AS
$func$
   SELECT s.*, a1.average_sed, a2.av_daily_sed
   FROM   "Sequence" s
   CROSS JOIN (  -- safe for an aggregate that always returns a row
      SELECT AVG("logSED")::real AS average_sed
      FROM   "Log_Alpha"
      WHERE  "logTime" >= tme1
      AND    "logTime" <  tme2
      ) a1
   LEFT JOIN LATERAL (
      SELECT AVG("logSED")::real AS av_daily_sed
      FROM   "Log_Alpha" a
      WHERE  a."logTime" >= (s."seqTime" - interval '1 day')
      and    a."logTime" <   s."seqTime" 
      ) a2 ON TRUE
   WHERE  s."seqMinute" = mn
   AND    s."seqTime" >= tme1
   AND    s."seqTime" <  tme2  -- I assume you want '<' like above
   ORDER  BY s."seqTime";
$func$  LANGUAGE sql STABLE;

唯一的区别是:"seqTime"会保持不变,而不是"seqTime" - '1 day'::interval,就像你的功能一样(不确定是否有意)。

相关:

旁白

没办法 来&#34;添加&#34;列到plpgsql中的任何行类型变量。无论是众所周知的行类型(%ROWTYPE)还是匿名record - 都可以重新分配,但一旦分配就不会添加列。

  

所以我已声明虚拟表(该术语是否正确?)rc为:

rc "Sequence"%rowtype;

The manual clarifies:

  

复合类型的变量称为行变量(或行型变量)。

阅读那一章(推荐!)时也注意到:

  

可以将行变量声明为与a的行具有相同的类型   现有的表格或视图,使用 table_name %ROWTYPE表示法;要么   它可以通过给出复合类型的名称来声明。 (因为每一个   table实际上有一个相同名称的关联复合类型   在PostgreSQL中无论你是否写%ROWTYPE都无关紧要。但   %ROWTYPE的表单更具便携性。)

我强烈建议您专门使用合法的小写不带引号的标识符。你的CaMeL案例标识符是一个加载的脚枪,特别是当你有时双引号,有时候不会。

答案 1 :(得分:0)

有两种解决方案:

  1. 使用合成

    create table foo1(a int, b int, c int);
    create type extended_foo AS (f foo1, a1 int, b1 int);
    create or replace function fx1()
    returns extended_foo as $$ select null::extended_foo $$
    language sql;
    
    postgres=# select * from fx1();
    ┌───┬────┬────┐
    │ f │ a1 │ b1 │
    ╞═══╪════╪════╡
    │   │    │    │
    └───┴────┴────┘
    
    postgres=# select (f).*, a1, b1 from fx1();
    ┌───┬───┬───┬────┬────┐
    │ a │ b │ c │ a1 │ b1 │
    ╞═══╪═══╪═══╪════╪════╡
    │   │   │   │    │    │
    └───┴───┴───┴────┴────┘
    
  2. 继承 - PostgreSQL支持表继承(但我不确定,如果它通常是想法,但对于这个用例它应该工作 - 注意:Postgres中的OOP来自旧学年龄并且更新完成) :

    create table foo2(a1 int, b1 int) inherits(foo1);
    create or replace function fx2()
    returns foo2 as $$ select null::foo2 $$
    language sql;
    
    postgres=# select * from fx2();
    ┌───┬───┬───┬────┬────┐
    │ a │ b │ c │ a1 │ b1 │
    ╞═══╪═══╪═══╪════╪════╡
    │   │   │   │    │    │
    └───┴───┴───┴────┴────┘