Fehler "Spalte existiert nicht" in einer SELECT mit JOIN- und GROUP BY-Abfrage


7

Ich verwende PostgreSQL 9.1 mit einer Ruby on Rails-Anwendung.

Ich versuche, die letzte Version jeder "Gebühr" (in meiner Verlaufstabelle: hist_version_charges) aufzulisten, die zu derselben Projekt-ID gehört (proj_sous_projet_id = 2).

Dadurch verwende ich die Aggregatfunktion max () und wende das Ergebnis auf eine JOIN-Funktion in derselben Tabelle an, in der PostgreSQL nicht berechtigt ist, die Spalten in der SELECT-Klausel zu verwenden, wenn sie nicht in der GROUP BY-Klausel ALTHOUGH mit max () meine natürlich interessiert mich die Zeile mit den Maximalwerten!

Das ist meine Frage:

SELECT h_v_charges.*, 
       max(last_v.version) as lv 
FROM hist_versions_charges h_v_charges 
    JOIN hist_versions_charges last_v 
      ON h_v_charges.version = lv 
    AND h_v_charges.proj_charge_id = last_v.proj_charge_id 
GROUP BY last_v.proj_sous_projet_id, 
         last_v.proj_charge_id 
HAVING last_v.proj_sous_projet_id = 2 
ORDER BY h_v_charges.proj_charge_id ASC;

Die Fehlermeldung, die ich bekam:

ERROR:  column "lv" does not exist
LINE 1: ..._versions_charges last_v ON h_v_charges.version = lv AND h_v...
                                                             ^
********** Error **********

ERROR: column "lv" does not exist
SQL state: 42703
Character: 147

Ich habe es auch mit "last_v.lv" versucht, aber der Fehler bleibt gleich.

Wenn jemand eine Vorstellung davon hat, was los ist, ist sie mehr als willkommen.

=== UPDATE ===

Laut den Antworten von * a_horse_with_no_name * und Colin 't Hart kam ich schließlich zu folgender Abfrage:

SELECT *
FROM (
    SELECT *, max(version) OVER (PARTITION BY proj_charge_id) AS lv
    FROM hist_versions_charges
    WHERE proj_sous_projet_id = 2) AS hv
WHERE hv.lv = hv.version
ORDER BY hv.proj_charge_id ASC;

Mit einem einzigen ORDER BY geht es etwas schneller.

Ich habe auch die Abfrage mit einer WITH-Klausel versucht. Obwohl "schöner", entsteht eine zusätzliche Bearbeitungsgebühr. Da ich weiß, dass ich die Unterabfrage in Zukunft nicht zweimal oder mehrmals in derselben Hauptabfrage wiederverwenden werde, kann ich eine einfache Unterabfrage problemlos verwenden.

Trotzdem danke an * a_horse_with_no_name * und Colin 't Hart . Ich habe viele Dinge gelernt!


2
" berechtigt nicht zur Verwendung der Spalten in der SELECT-Klausel, wenn sie nicht in der GROUP BY-Klausel enthalten sind ", ebenso wie jedes andere sinnvolle DBMS.
a_horse_with_no_name

Ja, aber wie hier erläutert: rpbouman.blogspot.se/2007/05/debunking-group-by-myths.html definiert der SQL-Standard von 2003, dass es möglich sein sollte, funktionsabhängige Spalten zu verwenden.
Douglas


1
@a_horse_with_no_name Du hast recht. Die obige Abfrage ist jedoch auch nach dem Standard von 2003 nicht gültig, da sie den Alias ​​(in der SELECTListe definiert ) in der FROMKlausel verwendet (eigentlich der ONTeil, der aber immer noch in der FROM-Klausel enthalten ist)!
Ypercubeᵀᴹ

@a_horse_with_no_name: Postgresql interpretiert den Standard "funktionsabhängig", indem der Begriff mit einem Primärschlüssel verknüpft wird. Ich spreche jedoch von "gesundem Menschenverstand". Wenn Sie einen max () -Wert finden, weiß postgresql bereits, in welcher Zeile dieser max () -Wert gespeichert ist. Postgresql sollte daher unabhängig von der GROUP BY-Klausel mit einer Aggregatfunktion wie max () auf alle Spalten zugreifen können . Dies ist nur eine Frage der Logik und des gesunden Menschenverstandes. Auf jeden Fall war meine Abfrage falsch, und vielen Dank, dass Sie mir auf den Klammerfehler hingewiesen haben.
Douglas

Antworten:


9

Sie möchten wahrscheinlich so etwas:

SELECT h_v_charges.*, 
       last_v.last_version
FROM hist_versions_charges h_v_charges 
  JOIN (select proj_charge_id, 
               max(version) as last_version
        from hist_versions_charges 
        where proj_sous_projet_id = 2  
        group by proj_charge_id
  ) last_v  
  ON h_v_charges.version = last_v.last_version
 AND h_v_charges.proj_charge_id = last_v.proj_charge_id 
ORDER BY h_v_charges.proj_charge_id ASC;

Eine möglicherweise (da kein Join erforderlich ist) schnellere Lösung wäre:

select *
from (
   select hvc.*, 
          row_number() over (partition by proj_charge_id order by version desc) as rn
   from hist_versions_charges as hvc
   where proj_sous_projet_id = 2  
) as hv
where rn = 1
order by hv.proj_charge_id ASC;

Wie Colin betont hat, kann dies auch geschrieben werden als:

with hv as (
  select hvc.*, 
         row_number() over (partition by proj_charge_id order by version desc) as rn
  from hist_versions_charges as hvc
  where proj_sous_projet_id = 2  
) 
select *
from hv
where rn = 1
order by hv.proj_charge_id ASC;

1
@Douglas: Wenn Sie Fensterfunktionen verwenden, erhalten Sie möglicherweise eine bessere Leistung. Sie können die Ausführungspläne vergleichen, um herauszufinden, welcher effizienter ist.
a_horse_with_no_name

Wow ! Fensterfunktionen. Dies ist eine brandneue Welt für mich. Es scheint prowerfull. Vielen Dank, ich werde dies gründlich studieren. Nur eine Frage, womit "hv" in Ihrer Abfrage zusammenhängt? Ich kann "hvc" sehen, das möglicherweise mit einer fehlenden AS-Klausel mit hist_versions_charges verknüpft ist. Ist die hv jedoch eine Funktion oder nur ein Tippfehler?
Douglas

1
@Douglas: hvist ein Alias ​​für die abgeleitete Tabelle (auch bekannt als " Unterabfrage "). Und Sie haben Recht, ich habe den Alias in der abgeleiteten Tabelle vergessen .
a_horse_with_no_name

Sie können die zweite Lösung noch schöner machen, indem Sie a verwenden with clause. Der große Vorteil besteht darin, dass Sie, wenn Sie in Zukunft entscheiden, dass Sie die Unterabfrage zweimal verwenden müssen, sie namentlich referenzieren können, während Sie beim Unterabfrageansatz die Unterabfrage wiederholen müssen.
Colin 't Hart

1
@ Colin'tHart: Ich weiß;) Ich wollte einfach nicht zu viele neue Dinge einführen (und ich wollte irgendwie in der Nähe der ursprünglichen Abfrage bleiben).
a_horse_with_no_name
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.