Создать таблицу из нескольких вызовов функции, возвращающей запись
У меня есть функция, которая делает некоторые базовые статистические данные по диапазону данных на основе меток времени начала и окончания:
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')
Но теперь у меня возникли проблемы с созданием другой функции или даже оператора select, который может объединять несколько вызовов функции 'cal'. Например, я хочу вернуть таблицу, которая содержит три периода времени. Таким образом, возвращение будет 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 ответа
Использовать 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 (для pg 8.3, чтобы продемонстрировать, что он работает в старых версиях).
Так как функции, возвращающие множество в списке SELECT, недовольны некоторыми и являются нестандартным 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 явно ссылается на предыдущую таблицу. Подробности:
SQL Fiddle для текущих Postgres 9.3.
Если ваши даты являются параметрами, вам нужно что-то вроде:
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