J'essaie d'écrire une fonction PL/pgSQL avec des arguments facultatifs. Il exécute une requête basée sur un ensemble filtré d'enregistrements (si spécifié), sinon, il exécute une requête sur l'ensemble de données dans une table.
Par exemple (CODE PSEUDO):
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;
Quelle serait la bonne façon de mettre en œuvre cette fonction?
En passant, je voudrais savoir comment je pourrais appeler une telle fonction dans une autre fonction extérieure. C'est ainsi que je le ferais - est-ce correct ou existe-t-il une meilleure façon?
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
Depuis PostgreSQL 8.4 (que vous semblez exécuter), il existe valeurs par défaut pour les paramètres de fonction . Si vous placez votre paramètre en dernier et fournissez une valeur par défaut, vous pouvez simplement l'omettre de l'appel:
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;
Points majeurs:
Le mot clé DEFAULT
est utilisé pour déclarer les paramètres par défaut. Alternative courte: =
.
J'ai supprimé le redondant param1
de l'exemple désordonné.
Depuis que vous revenez SELECT * FROM foobar
, déclarez le type de retour comme RETURNS SETOF foobar
au lieu de RETURNS SETOF record
. Ce dernier formulaire avec des enregistrements anonymes est très compliqué, vous devez fournir une liste de définition de colonne à chaque appel.
J'utilise un tableau d'entiers (int[]
) comme paramètre de fonction. Adapté l'expression IF
et la clause WHERE
en conséquence.
Les instructions IF
ne sont pas disponibles en SQL simple. Doit être LANGUAGE plpgsql
pour ça.
Appelez avec ou sans _ids
:
SELECT * FROM foofunc(1, '2012-1-1'::date);
Effectivement les mêmes:
SELECT * FROM foofunc(1, '2012-1-1'::date, '{}'::int[]);
Vous devez vous assurer que l'appel est sans ambiguïté. Si vous avez une autre fonction du même nom et deux paramètres, Postgres peut ne pas savoir lequel choisir. Le casting explicite (comme je le démontre) le réduit. Sinon, les littéraux de chaîne non typés fonctionnent aussi, mais être explicite ne fait jamais de mal.
Appel depuis une autre fonction:
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;
Élaborer sur la réponse de Frank sur ce fil:
L'argument VARIADIC
ne doit pas être le seul argument, seulement le dernier.
Vous pouvez utiliser VARIADIC
pour les fonctions qui peuvent prendre zéro argument variadique, c'est juste un peu plus compliqué en ce qu'il nécessite un style d'appel différent pour zéro args. Vous pouvez fournir une fonction wrapper pour masquer la laideur. Étant donné une définition initiale de la fonction varardique comme:
CREATE OR REPLACE FUNCTION foofunc(param1 integer, param2 date, param2 date, optional_list_of_ids VARIADIC integer[]) RETURNS SETOF RECORD AS $$
....
$$ language sql;
Pour zéro argument, utilisez un wrapper comme:
CREATE OR REPLACE FUNCTION foofunc(integer, date, date) RETURNS SETOF RECORD AS $body$
SELECT foofunc($1,$2,$3,VARIADIC ARRAY[]::integer[]);
$body$ LANGUAGE 'sql';
ou appelez simplement la fonction principale avec un tableau vide comme VARIADIC '{}'::integer[]
directement. L'emballage est moche, mais il est contenu laid, donc je recommanderais d'utiliser un emballage.
Les appels directs peuvent être effectués sous forme variadique:
SELECT foofunc(1,'2011-01-01','2011-01-01', 1, 2, 3, 4);
... ou forme d'appel tableau avec tableau ctor:
SELECT foofunc(1,'2011-01-01','2011-01-01', VARIADIC ARRAY[1,2,3,4]);
... ou forme littérale du texte du tableau:
SELECT foofunc(1,'2011-01-01','2011-01-01', VARIADIC '{1,2,3,4}'::int[]);
Les deux dernières formes fonctionnent avec des tableaux vides.
Vous voulez dire Fonctions SQL avec un nombre variable d'arguments ? Si c'est le cas, utilisez VARIADIC.