Необязательный аргумент в функции PL/pgSQL
Я пытаюсь написать функцию PL/pgSQL с необязательными аргументами. Он выполняет запрос на основе отфильтрованного набора записей (если указан), в противном случае выполняет запрос по всему набору данных в таблице.
Например (PSEUDO CODE):
CREATE OR REPLACE FUNCTION foofunc(param1 integer, param2 date, param2 date, optional_list_of_ids=[]) RETURNS SETOF RECORD AS $$
IF len(optional_list_of_ids) > 0 THEN
RETURN QUERY (SELECT * from foobar where f1=param1 AND f2=param2 AND id in optional_list_of_ids);
ELSE
RETURN QUERY (SELECT * from foobar where f1=param1 AND f2=param2);
ENDIF
$$ LANGUAGE SQL;
Каким будет правильный способ реализации этой функции?
В стороне, я хотел бы знать, как я мог бы назвать такую функцию в другой внешней функции. Вот как я это сделаю - это правильно, или есть лучший способ?
CREATE FUNCTION foofuncwrapper(param1 integer, param2 date, param2 date) RETURNS SETOF RECORD AS $$
BEGIN
CREATE TABLE ids AS SELECT id from foobar where id < 100;
RETURN QUERY (SELECT * FROM foofunc(param1, param2, ids));
END
$$ LANGUAGE SQL
Ответы
Ответ 1
Поскольку PostgreSQL 8.4 (который вы, похоже, выполняете), есть значения по умолчанию для параметров функции. Если вы поместите свой параметр последним и укажите значение по умолчанию, вы можете просто опустить его из вызова:
CREATE OR REPLACE FUNCTION foofunc(_param1 integer
, _param2 date
, _ids int[] DEFAULT '{}')
RETURNS SETOF foobar AS -- declare return type!
$func$
BEGIN -- required for plpgsql
IF _ids <> '{}'::int[] THEN -- exclude empty array and NULL
RETURN QUERY
SELECT *
FROM foobar
WHERE f1 = _param1
AND f2 = _param2
AND id = ANY(_ids); -- "IN" is not proper syntax for arrays
ELSE
RETURN QUERY
SELECT *
FROM foobar
WHERE f1 = _param1
AND f2 = _param2;
END IF;
END -- required for plpgsql
$func$ LANGUAGE plpgsql;
Основные моменты:
-
Ключевое слово DEFAULT
используется для объявления параметров по умолчанию. Короткая альтернатива: =
.
-
Я удалил избыточный param1
из грязного примера.
-
Поскольку вы возвращаете SELECT * FROM foobar
, объявите возвращаемый тип как RETURNS SETOF foobar
вместо RETURNS SETOF record
. Последняя форма с анонимными записями очень громоздка, вам нужно будет предоставить список определения столбцов при каждом вызове.
-
Я использую массив integer (int[]
) как параметр функции. Адаптировало выражение IF
и WHERE
соответственно.
-
Операторы IF
не доступны в простом SQL. Для этого должен быть LANGUAGE plpgsql
.
Вызов с или без _ids
:
SELECT * FROM foofunc(1, '2012-1-1'::date);
Эффективно то же самое:
SELECT * FROM foofunc(1, '2012-1-1'::date, '{}'::int[]);
Вы должны убедиться, что вызов однозначен. Если у вас есть другая функция с тем же именем и двумя параметрами, Postgres может не знать, что выбрать. Явное кастинг (как я демонстрирую) сужает его. Иные, нетипизированные строковые литералы тоже работают, но явное никогда не болит.
Вызов из другой функции:
CREATE FUNCTION foofuncwrapper(_param1 integer, _param2 date)
RETURNS SETOF foobar AS
$func$
DECLARE
_ids int[] := '{1,2,3}';
BEGIN
-- irrelevant stuff
RETURN QUERY
SELECT * FROM foofunc(_param1, _param2, _ids);
END
$func$ LANGUAGE plgpsql;
Ответ 2
Разрабатывая на Фрэнка ответ на эту тему:
VARIADIC
агит не должен быть единственным аргументом, только последним.
Вы можете использовать VARIADIC
для функций, которые могут принимать нулевые переменные аргументы, но это немного немного, потому что для нулевого args требуется другой стиль вызова. Вы можете предоставить функцию обертки, чтобы скрыть уродство. Учитывая начальное определение функции:
CREATE OR REPLACE FUNCTION foofunc(param1 integer, param2 date, param2 date, optional_list_of_ids VARIADIC integer[]) RETURNS SETOF RECORD AS $$
....
$$ language sql;
Для нулевых аргументов используйте обертку, например:
CREATE OR REPLACE FUNCTION foofunc(integer, date, date) RETURNS SETOF RECORD AS $body$
SELECT foofunc($1,$2,$3,VARIADIC ARRAY[]::integer[]);
$body$ LANGUAGE 'sql';
или просто вызовите главное func с пустым массивом, например VARIADIC '{}'::integer[]
. Обертка уродлива, но в ней содержится уродство, поэтому я рекомендую использовать обертку.
Прямые вызовы могут быть сделаны в вариационной форме:
SELECT foofunc(1,'2011-01-01','2011-01-01', 1, 2, 3, 4);
... или форму вызова массива с массивом ctor:
SELECT foofunc(1,'2011-01-01','2011-01-01', VARIADIC ARRAY[1,2,3,4]);
... или текстовая форма массива:
SELECT foofunc(1,'2011-01-01','2011-01-01', VARIADIC '{1,2,3,4}'::int[]);
Последние две формы работают с пустыми массивами.
Ответ 3
Вы имеете в виду SQL-функции с переменными числами аргументов? Если да, используйте VARIADIC.