Verwenden einer Alias-Spalte in der where-Klausel in Postgresql


73

Ich habe eine Frage wie diese:

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'

Was den folgenden Fehler ergibt:

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

In MySql ist dies gültig, aber anscheinend nicht in Postgresql. Soweit ich SELECTdas beurteilen kann, liegt der Grund darin, dass der Teil der Abfrage später als der WHERETeil ausgewertet wird . Gibt es eine gemeinsame Problemumgehung für dieses Problem?


Es ist eine gute Frage, aber eine bizarre Beispielabfrage. Sie möchten niemals einen Nicht-NULL-Wert für diese Spalte auswählen, daher ist die gesamte CASE-Anweisung völlig unnötig.
Phils

@phils Du hast recht. Die Abfrage wird dynamisch generiert, sodass die whereKlausel möglicherweise etwas anderes enthält, der selectTeil jedoch gleich bleibt.
Troelskn

Antworten:


16

Die Unterstützung von MySQL ist, wie Sie erfahren haben, nicht standardisiert. Der richtige Weg besteht darin, den gleichen Ausdruck wie in der SELECT-Klausel erneut zu drucken:

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

5
Außerdem können Sie die CASE-Anweisung durch COALESCE ersetzen : COALESCE(lead_informations.state, 'NEW') AS lead_state.
OMG Ponys

3
Es scheint etwas umständlich, die Logik so duplizieren zu müssen, aber ich denke, das mache ich dann einfach. Wusste nichts von COALESCE- Danke für diesen Tipp.
Troelskn

76

Ich hatte Probleme mit dem gleichen Problem und "MySQL-Syntax ist nicht Standard" ist meiner Meinung nach kein gültiges Argument. PostgreSQL fügt auch praktische, nicht standardmäßige Erweiterungen hinzu, z. B. "INSERT ... RETURNING ...", um nach dem Einfügen automatische IDs abzurufen. Auch das Wiederholen großer Abfragen ist keine elegante Lösung.

Ich fand die WITH-Anweisung jedoch sehr hilfreich. Es erstellt sozusagen eine temporäre Ansicht innerhalb der Abfrage, die Sie dann wie eine normale Tabelle verwenden können. Ich bin nicht sicher, ob ich Ihren JOIN richtig umgeschrieben habe, aber im Allgemeinen sollte es so funktionieren:

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'

15
Die Aussage, dass etwas "nicht dem Standard entspricht", ist völlig gültig, wenn Sie etwas für zwei verschiedene Produkte tun möchten. Beide implementieren Teile der SQL-Standards und haben nicht standardmäßige Erweiterungen. Erwarten Sie nicht, dass die nicht standardmäßigen Erweiterungen übersetzt werden. Erwarten Sie, dass die SQL-Standardteile übersetzt werden. Davon abgesehen - danke für das WITH-Beispiel.
Chaotisch

1
Es gibt auch einen Unterschied, wenn der Standard eins sagt und Sie etwas anderes tun als Erweiterungen des Produkts, bei dem der Standard still ist.
Kirk Roybal

Dies ist die richtige Antwort auf die Frage. Der einzige Grund, warum die akzeptierte Antwort funktioniert, besteht darin, dass die Spalte, in der sich die WHERE-Klausel befindet, tatsächlich in der verknüpften Tabelle vorhanden ist, die keinen Alias ​​einer von einer Abfrage erstellten Spalte wie einen Alias ​​einer Unterabfrage adressiert.
Scap

22

Sie müssten entweder die case-Anweisung in der where-Klausel duplizieren, oder ich bevorzuge Folgendes:

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')

2

Ich glaube, die übliche Lösung besteht darin, ein inneres SELECT für die Berechnung (oder in diesem Fall die CASE-Anweisung) zu verwenden, damit das Ergebnis des inneren SELECT für die gesamte äußere Abfrage verfügbar ist, wenn die Ausführung zu dieser Abfrage gelangt. Andernfalls wird die WHERE-Klausel zuerst ausgewertet und weiß nichts über die SELECT-Klausel.


0

Ich habe dort einen Alias ​​verwendet. (INNERE Abfrage).

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
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';

7
Antworten mit Code werden normalerweise am besten von einer Erklärung begleitet, warum das, was Sie gepostet haben, funktioniert und wie es die Frage speziell
anspricht

@ C.Nivs Der Kommentar ist selbsterklärend
jan4co

@GianGomen möglicherweise nicht an neue Benutzer, insbesondere wenn die Anleitung zur Beantwortung fehlte. Es hilft wirklich anderen Menschen, die später auf diese Antwort stoßen könnten. Es ist so, als ob Sie argumentieren könnten, dass die Richtlinien zum Stellen einer Frage selbsterklärend sind, aber schlechte Fragen häufig gestellt werden.
Wenn Sie also ein paar
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.