2015-03-04 2 views
2

Je suis en train de se joindre à la table et la fonction qui renvoie des lignes:JOIN sur ensemble fonction renvoyant résultats

SELECT p.id, p.name, f.action, f.amount 
FROM person p 
JOIN calculate_payments(p.id) f(id, action, amount) ON (f.id = p.id); 

Cette fonction retourne 0, 1 ou plusieurs lignes pour chaque id. La requête fonctionne sur PostgreSQL 9.3, mais sur 9.1 il montre l'erreur suivante:

ERROR: invalid reference to FROM-clause entry for table "p" 
HINT: There is an entry for table "p", but it cannot be referenced from this part of the query 

Je ne peux pas passer des calculs de la fonction dans la requête.
Je ne peux pas utiliser JOIN LATERAL qui est une nouvelle fonctionnalité de 9.3 si je comprends bien.
Y a-t-il une solution de contournement à ce problème?

Répondre

7

En Postgres 9.1:

SELECT name, (f).* -- note the parentheses! 
FROM (SELECT name, calculate_payments(id) AS f FROM person) sub; 

En supposant que votre fonction a un type de retour bien défini avec les noms de colonnes (id, action, amount) - information est manquante dans la question.
En supposant également que votre fonction renvoie toujours la même id il est alimenté (ce qui est redondant dans ce cas et pourrait être optimisé).

Le même sous forme beaucoup plus bavard:

SELECT sub.id, sub.name, (sub.f).action, (sub.f).amount -- parentheses! 
FROM (
    SELECT p.id, p.name, calculate_payments(p.id) AS f(id, action, amount) 
    FROM person p 
) sub; 

fonctions Set-retour dans le résultat de la liste SELECT en plusieurs lignes. Mais c'est une fonctionnalité non standard et un peu excentrique. La nouvelle fonctionnalité LATERAL de la page 9.3+ est préférable.

Vous pourrait décomposer le type de ligne dans la même étape:

SELECT *, (calculate_payments(p.id)).* -- parentheses! 
FROM person p 

Mais en raison d'une faiblesse dans le planificateur de requêtes Postgres, cela conduit à une évaluation de la fonction une fois par colonne:

Ou dans votre cas:

SELECT p.id, p.name 
    , (calculate_payments(p.id)).action 
    , (calculate_payments(p.id)).amount 
FROM person p 

Même problème: évaluation multiple.

Pour être précis, l'équivalent de la solution en pg 9.3+ est:

SELECT p.id, p.name, f.action, f.amount 
FROM person p 
LEFT JOIN LATERAL calculate_payments(p.id) f ON TRUE; 

lignes conservant dans le résultat lorsque la fonction renvoie 0 lignes.

Si cela ne vous intéresse pas, vous pouvez le simplifier à la page 9.3+:

SELECT p.id, p.name, f.action, f.amount 
FROM person p, calculate_payments(p.id) f; 

réponse étroitement liée:

+0

Vous @Ervin Merci je l'ai fait comme vous le suggérez: SELECT \t p.id, \t p.name , \t sous.action, \t sous.amount DE \t personne p \t INNER JOIN (SELECT (calculate_payments (p1.id)). * FROM personne p1) sous ON (p.id = sub.id) Quelque chose comme ça et ça a marché. – faskunji

+0

@faskunji: Sauf que c'est * pas * exactement ce que j'ai suggéré. La fonction est évaluée plusieurs fois par ligne de cette façon. Vous pouvez avoir moins cher. –