Това, което искате, еневъзможно . SQL е строго въведен език. Функциите на PostgreSQL трябва да декларират тип връщане (RETURNS ..
) ввреме насъздаване .
Ограничен начин за заобикаляне на това е с полиморфни функции. Ако можете да предоставите типа връщане в време на извикване на функцията . Но това не е видно от въпроса ви.
- Рефакторирайте функция PL/pgSQL, за да върнете резултатите от различни SELECT заявки
Вие можете върне напълно динамичен резултат с анонимни записи. Но тогава от вас се изисква да предоставите списък с дефиниции на колони с всяко повикване. А откъде знаеш за върнатите колони? Улов 22.
Има различни решения, в зависимост от това, от което се нуждаете или с какво можете да работите. Тъй като изглежда, че всичките ви колони с данни споделят един и същ тип данни, предлагам да върнете масив :text[]
. Или можете да върнете тип документ като hstore
или json
. Свързано:
-
Динамична алтернатива на завъртане с CASE и GROUP BY
-
Динамично преобразувайте ключовете на hstore в колони за неизвестен набор от ключове
Но може да е по-лесно просто да използвате две извиквания:1:Оставете Postgres да изгради заявката. 2:Изпълнете и извлечете върнатите редове.
- Избиране на множество max() стойности с помощта на един SQL израз
Не бих използвал функцията от Ерик Миникел, както е представена във вашия въпрос изобщо . Не е безопасно срещу SQL инжектиране чрез злонамерено деформирани идентификатори. Използвайте format()
за създаване на низове на заявка, освен ако не използвате остаряла версия, по-стара от Postgres 9.1.
По-кратка и по-чиста реализация може да изглежда така:
CREATE OR REPLACE FUNCTION xtab(_tbl regclass, _row text, _cat text
, _expr text -- still vulnerable to SQL injection!
, _type regtype)
RETURNS text AS
$func$
DECLARE
_cat_list text;
_col_list text;
BEGIN
-- generate categories for xtab param and col definition list
EXECUTE format(
$$SELECT string_agg(quote_literal(x.cat), '), (')
, string_agg(quote_ident (x.cat), %L)
FROM (SELECT DISTINCT %I AS cat FROM %s ORDER BY 1) x$$
, ' ' || _type || ', ', _cat, _tbl)
INTO _cat_list, _col_list;
-- generate query string
RETURN format(
'SELECT * FROM crosstab(
$q$SELECT %I, %I, %s
FROM %I
GROUP BY 1, 2 -- only works if the 3rd column is an aggregate expression
ORDER BY 1, 2$q$
, $c$VALUES (%5$s)$c$
) ct(%1$I text, %6$s %7$s)'
, _row, _cat, _expr -- expr must be an aggregate expression!
, _tbl, _cat_list, _col_list, _type
);
END
$func$ LANGUAGE plpgsql;
Извикване на същата функция като оригиналната версия. Функцията crosstab()
се предоставя от допълнителния модул tablefunc
който трябва да бъде инсталиран. Основи:
- PostgreSQL Crosstab Query
Това обработва имената на колони и таблици безопасно. Обърнете внимание на използването на типове идентификатори на обект regclass
и regtype
. Работи и за квалифицирани по схема имена.
- Име на таблица като параметър на функцията на PostgreSQL
Въпреки това, тоне е напълно безопасно докато предавате низ, който да бъде изпълнен като израз (_expr
- cellc
в първоначалната си заявка). Този вид въвеждане е по своята същност опасно срещу SQL инжектиране и никога не трябва да се излага на широката публика.
- SQL инжекция във функциите на Postgres срещу подготвени заявки
Сканира таблицата само веднъж за двата списъка с категории и трябва да е малко по-бърз.
Все още не може да върне напълно динамични типове редове, тъй като това е абсолютно невъзможно.