Вашата функция може да изглежда така:
CREATE FUNCTION select_transactions3(_col text, _val text, _limit int)
RETURNS SETOF transactions AS
$BODY$
BEGIN
RETURN QUERY EXECUTE '
SELECT *
FROM transactions
WHERE ' || quote_ident(_col) || ' = $1
LIMIT $2'
USING _val, _limit;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
В PostgreSQL 9.1 или по-късно, което е по-лесно с format()
...
RETURN QUERY EXECUTE format('
SELECT *
FROM transactions
WHERE %I = $1
LIMIT $2', _col)
USING _val, _limit;
...
%I
избягва идентификатори като quote_ident()
.
Основни точки:
-
Натъквахте се на ограничението на динамичния SQL, че не можете да използвате параметри за идентификатори. Трябва да създадете низа на заявката с името на колоната и след това изпълнете го.
-
Можете обаче да направите това със стойности. Демонстрирам използването на
USING
клауза заEXECUTE
. Също така обърнете внимание на използването наquote_ident()
:предотвратява SQL инжектиране и определени синтактични грешки. -
Също така до голяма степен опростих вашата функция.
[RETURN QUERY EXECUTE][3]
прави вашия код по-кратък и по-бърз. Няма нужда да правите цикъл, ако всичко, което правите, е да върнете реда. -
Използвам named
IN
параметри, така че да не се объркате с $-нотацията в низа на заявката.$1
и$2
вътре в низа на заявката се обърнете към стойностите, предоставени вUSING
клауза, а не към входните параметри. -
Променям на
SELECT *
тъй като така или иначе трябва да върнете целия ред, за да съответства на декларирания тип връщане. -
Последно, но не на последно място:Не забравяйте да вземете под внимание какво казва ръководството относно функциите, декларирани
SECURITY DEFINER
.
ВРЪЩАН ТИП
Ако не искате да върнете целия ред, една удобна възможност е:
CREATE FUNCTION select_transactions3(_col text, _val text, _limit int)
RETURNS TABLE (invoice_no varchar(125), amount numeric(12,2) AS ...
Тогава не е нужно да предоставяте списък с дефиниции на колони с всяко извикване и можете да опростите до:
SELECT * FROM select_to_transactions3('invoice_no', '1103300105472', 1);