web-dev-qa-db-fra.com

Utilisation d'une colonne Alias ​​dans la clause where dans Postgresql

J'ai une requête comme celle-ci:

SELECT
    jobs.*, 
    (
        CASE
            WHEN lead_informations.state IS NOT NULL THEN lead_informations.state
            ELSE 'NEW'
        END
    ) AS lead_state
FROM
    jobs
    LEFT JOIN lead_informations ON
        lead_informations.job_id = jobs.id
        AND
        lead_informations.mechanic_id = 3
WHERE
    lead_state = 'NEW'

Ce qui donne l'erreur suivante:

PGError: ERROR:  column "lead_state" does not exist
LINE 1: ...s.id AND lead_informations.mechanic_id = 3 WHERE (lead_state...

En MySql, cela est valable, mais apparemment pas en Postgresql. D'après ce que je peux comprendre, la raison en est que la partie SELECT de la requête est évaluée plus tard que la partie WHERE. Existe-t-il une solution de contournement commune à ce problème?

51
troelskn

Le support de MySQL est, comme vous l'avez expérimenté, non standard. La méthode correcte consiste à réimprimer la même expression utilisée dans la clause SELECT:

SELECT
    jobs.*, 
    CASE 
         WHEN lead_informations.state IS NOT NULL THEN lead_informations.state 
         ELSE 'NEW' 
    END AS lead_state
FROM
    jobs
    LEFT JOIN lead_informations ON
        lead_informations.job_id = jobs.id
        AND
        lead_informations.mechanic_id = 3
WHERE
    lead_informations.state IS NULL
12
OMG Ponies

Je me suis débattu sur le même problème et "la syntaxe mysql n'est pas standard" n'est pas un argument valable à mon avis. PostgreSQL ajoute également des extensions pratiques non standard, par exemple "INSERT ... RETURNING ..." pour obtenir les identifiants automatiques après les insertions. De plus, la répétition de requêtes volumineuses n’est pas une solution élégante.

Cependant, j'ai trouvé la déclaration WITH très utile. Cela crée en quelque sorte une vue temporaire dans la requête que vous pouvez utiliser comme une table habituelle. Je ne suis pas sûr d'avoir bien réécrit votre JOIN, mais en général, il devrait fonctionner comme ceci:

WITH jobs_refined AS (
    SELECT
        jobs.*,
        (SELECT CASE WHEN lead_informations.state IS NOT NULL THEN lead_informations.state ELSE 'NEW' END) AS lead_state
    FROM jobs
    LEFT JOIN lead_informations
        ON lead_informations.job_id = jobs.id
        AND lead_informations.mechanic_id = 3
)
SELECT *
FROM jobs_refined
WHERE lead_state = 'NEW'
53
Marten Lehmann

Vous devez dupliquer l'instruction case dans la clause where ou ma préférence est de procéder de la manière suivante:

SELECT *
FROM (
SELECT 
    jobs.*, 
    (CASE WHEN lead_informations.state IS NOT NULL THEN lead_informations.state ELSE 'NEW' END) as lead_state
FROM 
    "jobs"
    LEFT JOIN lead_informations ON lead_informations.job_id = jobs.id
    AND lead_informations.mechanic_id = 3
) q1
WHERE (lead_state = 'NEW')
17
mrSpear

J'ai utilisé alias dans où comme ça. (Requête interne).

Select "Vendors"."VendorId", "Vendors"."Name","Result"."Total" 
From (Select "Trans"."VendorId", ("Trans"."A"+"Trans"."B"+"Trans"."C")    AS "Total"
        FROM "Trans"
    WHERE "Trans"."Year"=2014                                                
    ) As "Result"
JOIN "Vendors" ON "Result"."VendorId"="Vendors"."VendorId" 
WHERE "Vendors"."Class"='I' AND "Result"."Total" > 200
0
M Sohail Maroof

Je pense que la solution courante consiste à utiliser un SELECT interne pour le calcul (ou une instruction CASE dans ce cas) afin que le résultat du SELECT interne soit disponible pour l'ensemble de la requête externe au moment où l'exécution parvient à cette requête. Sinon, la clause WHERE est évaluée en premier et ne connaît rien de la clause SELECT.

0
David
SELECT "tab_1"."BirthDate", "tab_1"."col_1" FROM (
   SELECT BirthDate, DATEADD(year, 18, BirthDate) AS "col_1" FROM Employees
) AS "tab_1"
WHERE "tab_1"."col_1" >= '2000-12-31';
0
Douglas Rosa