J'ai des tables qui contiennent le même type de données pour chaque année, mais les données collectées varient légèrement en ce qu'elles n'ont pas les mêmes champs.Essayer de créer des chaînes de requête dynamiques avec PL/PgSQL pour créer des fonctions DRY dans PostgreSQL 9.6
d_abc_2016
d_def_2016
d_ghi_2016
d_jkl_2016
Il y a certaines constantes pour chaque table: company_id
, employee_id
, salary
. Cependant, chacun peut ou non avoir ces champs qui sont utilisés pour calculer les incitations totales: bonus
, commission
, cash_incentives
Il y en a beaucoup plus, mais juste en les utilisant comme exemple. Tous numeric
Je dois souligner à ce stade, les utilisateurs ont seulement la possibilité d'exécuter SELECT
déclarations.
Ce que je voudrais être en mesure de faire est la suivante:
- donner à l'utilisateur la possibilité d'appeler en
SELECT
et préciser leurs propres champs, en plus de l'appel - passer le nom de la table utilisée dans la fonction à utiliser en logique conditionnelle pour déterminer comment la chaîne de requête doit être construite pour le calcul
total_incentives
en plus de passer la table entière afin qu'une tonne d'arguments ne doive pas être passée dans la fonction
Fondamentalement ceci:
SELECT employee_id, salary, total_incentives(t, 'd_abc_2016')
FROM d_abc_2016 t;
Ainsi, la fonction appelée calculera total_incentives
qui est numeric
pour cette employee_id
et montrent aussi leur salary
. Mais l'utilisateur peut choisir d'ajouter d'autres champs à regarder.
Pour la fonction, parce que les champs utilisés dans la fonction total_incentives
varient d'une table à l'autre, j'ai besoin de créer une logique pour construire la chaîne de requête dynamiquement.
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS numeric AS
$$
DECLARE
-- table name lower case in case user typed wrong
tbl varchar(255) := lower($2;
-- parse out the table code to use in conditional logic
tbl_code varchar(255) := split_part(survey, '_', 2);
-- the starting point if the query string
base_calc varchar(255) := 'salary + '
-- query string
query_string varchar(255);
-- have to declare this to put computation INTO
total_incentives_calc numeric;
BEGIN
IF tbl_code = 'abc' THEN
query_string := base_calc || 'bonus';
ELSIF tbl_code = 'def' THEN
query_string := base_calc || 'bonus + commission';
ELSIF tbl_code = 'ghi' THEN
-- etc...
END IF;
EXECUTE format('SELECT $1 FROM %I', tbl)
INTO total_incentives_calc
USING query_string;
RETURN total_incentives_calc;
END;
$$
LANGUAGE plpgsql;
Il en résulte une:
ERROR: invalid input syntax for type numeric: "salary + bonus"
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 16 at EXECUTE
Comme il devrait être de retour un ensemble de valeurs numeric
. Remplacez-le par le suivant:
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS SETOF numeric AS
$$
...
RETURN;
Obtenez la même erreur.
Figure bien, c'est peut-être une table qu'il essaie de retourner.
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS TABLE(tot_inc numeric) AS
$$
...
Obtenez la même erreur. Vraiment, toute variation produit ce résultat. Donc vraiment pas sûr de savoir comment faire fonctionner ça.
Regardez RESULT QUERY
ou .
https://www.postgresql.org/docs/9.6/static/plpgsql-control-structures.html
RESULT QUERY
ne fonctionnera pas, car il faut une requête codée en dur de ce que je peux dire, qui ne prendra pas des variables.
RESULT NEXT
RESULT NEXT
itère à travers chaque enregistrement, ce qui, je ne pense pas, sera adapté à mes besoins et semble être très lent ... et il faut une requête codée dur de ce que je peux dire.
RESULT QUERY EXECUTE
semble prometteur.
-- EXECUTE format('SELECT $1 FROM %I', tbl)
-- INTO total_incentives_calc
-- USING query_string;
RETURN QUERY
EXECUTE format('SELECT $1 FROM %I', tbl)
USING query_string;
et obtenez:
ERROR: structure of query does not match function result type
DETAIL: Returned type character varying does not match expected type numeric in column 1.
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 20 at RETURN QUERY
Il devrait être de retour numeric
. Enfin, je peux obtenir ceci pour fonctionner, mais ce ne sera pas DRY. Je préfère ne pas faire un tas de fonctions séparées pour chaque table avec un code duplicatif. La plupart des exemples de travail que j'ai vu que toute requête dans la fonction et sont appelés comme tels:
SELECT total_incentives(d_abc_2016, 'd_abc_2016');
donc des colonnes supplémentaires devraient être spécifiées dans la fonction:
EXECUTE format('SELECT employee_id...)
Compte tenu les utilisateurs ne seront en mesure d'exécuter SELECT
dans la requête ce n'est vraiment pas une option. Ils doivent spécifier toutes les colonnes supplémentaires qu'ils souhaitent voir dans une requête.
J'ai posté une question similaire mais on m'a dit que ce n'était pas clair, alors j'espère que cette version plus longue expliquera plus clairement ce que j'essaie de faire.
Cool, la requête a été exécutée. Merci pour l'aide! Maintenant, pour trier et l'empêcher de multiplier la quantité d'enregistrements retournés par le nombre total d'enregistrements. – sockpuppet