Пишу функцию, результатом которой , грубо говоря, должен быть результат SELECT. Использую pgAdmin. PostgreSQL 9.1. Код моей функции следующий:
-- Function: get_data(character varying, timestamp without time zone, timestamp without time zone, character varying)
CREATE OR REPLACE FUNCTION get_data(param_name character varying, date_from timestamp without time zone, date_to timestamp without time zone, table_type character varying) RETURNS SETOF record AS $BODY$ DECLARE table_list RECORD; rec2 RECORD; dev_name varchar(32); -- имя устройства а-ля 'sect1' table_name varchar(64); -- полное имя таблицы а-ля '2013_10_sect1_sec' table_year text; -- выдранный год для имени таблицы table_month text; -- выдранный месяц для имени таблицы column_name varchar(64); -- имя тега = имя столбца а-ля 'u_' i int; -- счетчик для цикла j int; -- счетчик для цикла table_year_from int; -- год для имени таблицы: начало table_year_to int; -- год для имени таблицы: конец table_month_from int; -- месяц для имени таблицы: начало table_month_to int; -- месяц для имени таблицы: конец table_name_temp varchar(64); -- временное имя таблицы BEGIN --- выдираем имя устройства из имени тега dev_name := split_part(param_name, '_', 1); -- выдираем из 'G7_aeo_apwr_val' часть 'G7' dev_name := lower(dev_name); --- получаем имя тега=колонки column_name := REPLACE(lower(param_name), dev_name||'_', ''); -- имя столбца = ф-ля 'G1_gas_mass_flow' => 'gas_mass_flow' --- формируем список таблиц годных для выборки данных --- table_year_from := 1; FOR i IN date_part('year', date_from)::int .. date_part('year', date_to)::int LOOP FOR j IN date_part('month', date_from)::int .. date_part('month', date_to)::int loop table_name_temp := i || '_' || j || '_' || dev_name || '_' || table_type; FOR table_list IN EXECUTE 'SELECT tablename FROM pg_tables WHERE tablename = ''' || table_name_temp || ''';' LOOP --RETURN NEXT table_list; -- складываем годную строку в буфер --- выдираем год из date_from --- --table_year := date_part('year', date_from); --- выдираем месяц date_from --- --table_month := date_part('month', date_from); --- формируем полное имя таблицы а-ля '2013_10_sect1_hour' --- --table_name := table_year || '_' || table_month || '_' || dev_name || '_' || table_type; -- где $4 - тип таблицы 'sec/min/hour' table_name := table_list.tablename; FOR rec2 IN EXECUTE 'select dateandtime, ' || column_name || '_val, ' || column_name || '_quality' || ' from "' || table_name || '" where dateandtime BETWEEN ''' || date_from || ''' AND ''' || date_to || ''' ORDER BY dateandtime ASC;' LOOP RETURN NEXT rec2; -- складываем годную строку в буфер END LOOP; END LOOP; end loop; END LOOP; RETURN; END; $BODY$ LANGUAGE plpgsql VOLATILE COST 100 ROWS 1000; ALTER FUNCTION get_data(character varying, timestamp without time zone, timestamp without time zone, character varying) OWNER TO db_admin;
Если ее выполнить как
SELECT get_data('G1_exhaust_port_temperature_11', '2013-10-14 11:00:00', '2013-10-14 15:00:00', 'min')
, то в результате получаю ошибку
ERROR: set-valued function called in context that cannot accept a set
CONTEXT: PL/pgSQL function "get_data" line 49 at RETURN NEXT
********** Ошибка **********
ERROR: set-valued function called in context that cannot accept a set
SQL-состояние: 0A000
Контекст: PL/pgSQL function "get_data" line 49 at RETURN NEXT
Если же ее выполнить как
SELECT * FROM get_data('G1_exhaust_port_temperature_11', '2013-10-14 11:00:00', '2013-10-14 15:00:00', 'min') AS val(d timestamp, v real, q varchar)
то возвращаются данные в трех столбцах d, v, q
Вопрос: можно сделать эту функцию сразу возвращающей три стобца, которые в SELECT'е , без псевдонима "as val(d timestamp, v real, q varchar)" ?
Ты возвращаешь SETOF record -
Ты возвращаешь SETOF record - набор неких данных, организованных в таблицу/представление. Поэтому ты и не можешь использовать синтаксис подобный этому:
SELECT function_returning_setof() AS some_column
Правда, я встречал исключение: подобная функция возвращала, но массив, например результат данной функции был бы таков:
Однако даже это, как я понимаю, у тебя не подойдёт, потому что в твоём примере используется RETURN NEXT.
Иначе говоря, используй функции правильно:
SELECT * FROM function_returning_setof() AS some_column
Поэтому ты и не можешь
Поэтому ты и не можешь использовать синтаксис подобный этому:
SELECT function_returning_setof() AS some_column
это с чего следует ? И почему именно такой синтаксис приведен как невозможный ? Я хотел немного другой..... я то хочу функцию-обертку для обычного SELECT
record - я понимаю как одну строку из нескольких столбцов
SET OF record - соотвественно несколько строк, что есть таблица, что мне и нужно...... Если я ошибаюсь, то каково же истинное толкование "SET OF record" ?
потому что в твоём примере используется RETURN NEXT
Точного ответа как сделать нужную мне функцию я в инете не нашел - был даже вариант , где в качестве возвращаемого результат была объявлена таблица, однако этот пример у меня не заработал.... а то что в итоге получилось - это компиляция примеров с инета и результатов моих проб...... Так как же надо по-правильному ?
http://wiki.postgresql.org/wi
http://wiki.postgresql.org/wiki/Return_more_than_one_row_of_data_from_PL...
пример вызова функции без определения колонок
create or replace function get_current_rec(p_1 int)
returns table(a int, b int, c int) as
$$
declare
t int;
begin
t := 100;
RETURN QUERY
select 1 a, t b, p_1 c
union all
select t a, 20 b, p_1 c;
end;
$$
language 'plpgsql';
или вот другой пример:
create or replace function get_current_rec(p_1 int, out p_a int , out p_b int , out p_c int ,)
returns setof record as
$$
declare
t int;
begin
t := 100;
RETURN QUERY
select 1 a, t b, p_1 c
union all
select t a, 20 b, p_1 c;
end;
$$
language 'plpgsql';
но роще всего если надо обернуть select в функцию использовать язык SQL , а не plpgsql в определении функции.