从多次调用函数返回记录创建表

时间:2014-08-13 14:30:26

标签: sql function postgresql set-returning-functions lateral

我有一个函数可以根据开始和停止时间戳对一系列数据进行一些基本统计:

CREATE OR REPLACE FUNCTION cal(TIMESTAMP, TIMESTAMP, OUT Date_Time timestamp with time zone, OUT avg numeric, OUT stddev numeric, OUT Rstedv_per numeric) 
AS $$
SELECT
    max(datetime) as Date_Time,
    avg(SO2) AS Mean,
    stddev_samp(so2) as STD_DEV,
    stddev_samp(so2)/avg(SO2)*100 as Rstedv_Per
FROM Table43
WHERE datetime > $1 AND datetime < $2;
$$
 LANGUAGE SQL;

这适用于简单的单一选择,例如:

select * FROM
 cal('2014-08-02 05:29:00', '2014-08-02 05:32:00')

但现在我无法创建另一个函数,甚至是一个可以组合多个'cal'函数调用的select语句。例如,我想返回一个包含三个时间段的表。所以返回将是4列乘3行:

'2014-08-02 05:29:00', '2014-08-02 05:32:00'
'2014-08-02 05:35:00', '2014-08-02 05:39:00'
'2014-08-02 05:45:00', '2014-08-02 05:49:00'

2 个答案:

答案 0 :(得分:0)

如果您的日期是参数,则需要以下内容:

select * 
FROM cal('2014-08-02 05:29:00', '2014-08-02 05:32:00')
union all
select * 
FROM cal('2014-08-02 05:35:00', '2014-08-02 05:39:00')
union all
select * 
FROM cal('2014-08-02 05:45:00', '2014-08-02 05:39:00')

PostgreSQL 9.0 +

select cal(a, b)
from 
    values( 
    ('2014-08-02 05:29:00', '2014-08-02 05:32:00'),
    ('2014-08-02 05:29:00', '2014-08-02 05:32:00'),
    ('2014-08-02 05:29:00', '2014-08-02 05:32:00')
    ) as dates (a, b)

如果您的日期来自一张桌子,并且您正在使用 Postresql 9.3

select cal.*
FROM 
  (select '2014-08-02 05:29:00' a, '2014-08-02 05:32:00' b
      from foo_table) as dates
left join lateral cal(dates.a, dates.b) cal on true

答案 1 :(得分:0)

使用VALUES表达式提供多行输入日期。 然后......

适用于Postgres的所有版本

SELECT cal(a, b)
FROM  (
   VALUES 
      ('2014-08-02 05:29'::timestamp, '2014-08-02 05:32'::timestamp)
    , ('2014-08-02 05:35', '2014-08-02 05:39')
    , ('2014-08-02 05:45', '2014-08-02 05:39')
   ) v(a, b);

您可以将VALUES表达式替换为实际表格。

这会将整行作为单个列(而不是单个列)返回。您可以使用(cal(a, b)).*进行分解。虽然这有效,但效率低下。由于Postgres解析器的弱点,这将导致对函数的多次评估。详细解释:

相反,请使用子查询以获得更好的性能:

SELECT (rec).*
FROM  (
    SELECT cal(a, b)
    FROM  (
       VALUES 
          ('2014-08-02 05:29'::timestamp, '2014-08-02 05:32'::timestamp)
        , ('2014-08-02 05:35', '2014-08-02 05:39')
        , ('2014-08-02 05:45', '2014-08-02 05:39')
       ) v(a, b)
   ) sub;

SQL Fiddle(对于第8.3页,证明它适用于旧版本)。

由于SELECT列表中的set-returns函数被一些人所厌恶,并且是非标准SQL。

Postgres 9.3 +

这是Postgres 9.3引入的主要原因(符合SQL标准)LATERAL JOIN

SELECT f.*
FROM  (
   VALUES 
      ('2014-08-02 05:29'::timestamp, '2014-08-02 05:32'::timestamp)
    , ('2014-08-02 05:35', '2014-08-02 05:39')
    , ('2014-08-02 05:45', '2014-08-02 05:39')
   ) v(a,b)
  , cal(v.a, v.b) f;

此处LATERAL JOIN 隐式,因为FROM列表中的第二项明确引用了上一个表。详细说明:

目前的Postgres 9.3

SQL Fiddle