Почему функция не может сразу вернуть результат SELECT как таблицу ?

Пишу функцию, результатом которой , грубо говоря, должен быть результат 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
Правда, я встречал исключение: подобная функция возвращала, но массив, например результат данной функции был бы таков:

some_column
-------------
'{1,2,3}'

Однако даже это, как я понимаю, у тебя не подойдёт, потому что в твоём примере используется 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

Изображение Игорь

пример вызова функции без определения колонок

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 в определении функции.

Опции просмотра комментариев

Выберите предпочитаемый вами способ показа комментариев и нажмите "Сохранить настройки" для активации изменений.

Back to top

(С) Виктор Вислобоков, 2008-2023