PostgreSQL GROUP BY anders als MySQL?

70

Ich habe einige meiner MySQL-Abfragen nach PostgreSQL migriert, um Heroku zu verwenden. Die meisten meiner Abfragen funktionieren einwandfrei, aber ich habe immer wieder einen ähnlichen wiederkehrenden Fehler, wenn ich group by verwende:

FEHLER: Die Spalte "XYZ" muss in der GROUP BY-Klausel erscheinen oder in einer Aggregatfunktion verwendet werden

Könnte mir jemand sagen, was ich falsch mache?


MySQL, das zu 100% funktioniert:

SELECT `availables`.*
FROM `availables`
INNER JOIN `rooms` ON `rooms`.id = `availables`.room_id
WHERE (rooms.hotel_id = 5056 AND availables.bookdate BETWEEN '2009-11-22' AND '2009-11-24')
GROUP BY availables.bookdate
ORDER BY availables.updated_at


PostgreSQL-Fehler:

ActiveRecord :: StatementInvalid: PGError: ERROR: Die Spalte "Availables.id" muss in der GROUP BY-Klausel enthalten sein oder in einer Aggregatfunktion verwendet werden:
SELECT "Available". * FROM "Available" INNER JOIN "Zimmer" ON "Zimmer". id = "available" .room_id WHERE (rooms.hotel_id = 5056 AND available.bookdate ZWISCHEN E'2009-10-21 'UND E'2009-10-23') GROUP BY available.bookdate ORDER BY available.updated_at


Ruby-Code, der das SQL generiert:

expiration = Available.find(:all,
    :joins => [ :room ],
    :conditions => [ "rooms.hotel_id = ? AND availables.bookdate BETWEEN ? AND ?", hostel_id, date.to_s, (date+days-1).to_s ],
    :group => 'availables.bookdate',
    :order => 'availables.updated_at')  


Erwartete Ausgabe (von der funktionierenden MySQL-Abfrage):

+ ----- + ------- + ------- + ------------ + --------- + ---- ----------- + --------------- +
| id | Preis | Flecken | Buchdatum | room_id | created_at | aktualisierte_at |
+ ----- + ------- + ------- + ------------ + --------- + ---- ----------- + --------------- +
| 414 | 38,0 | 1 | 2009-11-22 | 1762 | 20.11.2009 ... | 20.11.2009 ... |
| 415 | 38,0 | 1 | 2009-11-23 | 1762 | 20.11.2009 ... | 20.11.2009 ... |
| 416 | 38,0 | 2 | 24.11.2009 | 1762 | 20.11.2009 ... | 20.11.2009 ... |
+ ----- + ------- + ------- + ------------ + --------- + ---- ----------- + --------------- +
3 Reihen im Set
gehalten
quelle
sooo ... wäre es besser, wenn ich die eindeutige Funktion am Buchdatum verwenden würde? Wenn ich das tun würde, würde ich die group by-Klausel trotzdem brauchen?
Holden
2
DISTINCTist langsamer als GROUP BY. Sie sollten also vorsichtig sein und eine GROUP BYLösung bevorzugen , wenn dies möglich ist.
Franz

Antworten:

110

MySQLs völlig nicht standardkonforme GROUP BYkönnen von Postgres emuliert werden DISTINCT ON. Bedenken Sie:

MySQL:

SELECT a,b,c,d,e FROM table GROUP BY a

Dies liefert 1 Zeile pro Wert von a(welche, die Sie nicht wirklich kennen). Nun, eigentlich können Sie raten, weil MySQL nichts über Hash-Aggregate weiß, also wird es wahrscheinlich eine Sortierung verwenden ... aber es wird nur weiter sortieren a, so dass die Reihenfolge der Zeilen zufällig sein könnte. Es sei denn, es wird ein mehrspaltiger Index anstelle der Sortierung verwendet. Jedenfalls wird es von der Abfrage nicht angegeben.

Postgres:

SELECT DISTINCT ON (a) a,b,c,d,e FROM table ORDER BY a,b,c

Dies liefert 1 Zeile pro Wert von a. Diese Zeile ist die erste in der Sortierung gemäß der in ORDER BYder Abfrage angegebenen. Einfach.

Beachten Sie, dass es sich hier nicht um ein Aggregat handelt, das ich berechne. Also GROUP BYmacht eigentlich eigentlich keinen Sinn. DISTINCT ONmacht viel mehr Sinn.

Rails ist mit MySQL verheiratet, daher wundert es mich nicht, dass SQL generiert wird, das in Postgres nicht funktioniert.

Bobflux
quelle
6
Hinzu kommt, dass in Postgres 9.1 nicht alle Spalten aufgelistet werden können, wenn der Primärschlüssel der Tabelle Teil der group byKlausel ist.
Denis de Bernardy
5
Laut diesem Artikel "Entlarven von GROUP BY-Mythen" hat dies nichts mit "nicht standardkonformen GROUP BY" zu tun.
Rafa
5
Laut diesem Artikel ist GROUP BY von MySQL immer noch nicht mit beiden Versionen des Standards kompatibel, da nicht überprüft wird, ob die zusätzlichen Spalten in der Auswahlliste von der Gruppe nach Spalten abhängig sind. Es gibt falsche Daten ohne Warnung aus (kann aber auch nützliche Zwecke erfüllen). PG 9.1 geht davon aus, dass das Einbeziehen der PK einer Tabelle bedeutet, dass alle anderen Spalten abhängig sind, was richtig ist. Dies deckt nicht den Standard 100% ab (andere korrekte Abfragen können als Fehler gekennzeichnet sein), deckt jedoch die meisten Anwendungsfälle ab, ohne falsche Ergebnisse zurückzugeben ...
Bobflux
7
"Rails ist mit MySQL verheiratet, daher wundert es mich nicht, dass SQL generiert wird, das in Postgres nicht funktioniert." Ich denke nicht, dass dies mehr zutrifft, da Postgres aufgrund seiner noSQL-Funktionen in der Rails-Community sehr beliebt geworden ist.
Yagooar
4
Rails ist nicht mehr mit MySQL verheiratet.
Superleuchte
17

PostgreSQL ist SQL-kompatibler als MySQL. Alle Felder - außer dem berechneten Feld mit Aggregationsfunktion - in der Ausgabe müssen in der GROUP BY-Klausel vorhanden sein.

Erlock
quelle
11

GROUP BY von MySQL kann ohne Aggregatfunktion verwendet werden (was dem SQL-Standard widerspricht) und gibt die erste Zeile in der Gruppe zurück (ich weiß nicht anhand welcher Kriterien), während PostgreSQL eine Aggregatfunktion haben muss (MAX, SUM usw.) in der Spalte, in der die GROUP BY-Klausel ausgegeben wird.

Bozho
quelle
5

Richtig, die Lösung, um dies zu beheben, besteht darin, Folgendes auszuwählen und auszuwählen, und wählen Sie jedes Feld aus, mit dem Sie das resultierende Objekt dekorieren möchten, und gruppieren Sie es nach diesen.

Böse - aber es ist, wie Gruppieren nach funktionieren sollte , im Gegensatz dazu, wie MySQL damit arbeitet, indem Sie erraten, was Sie meinen, wenn Sie keine Felder in Ihrer Gruppe durchkleben.

Omar Qureshi
quelle
1
Ich nehme an, MySQL hat mich verwöhnt oder ruiniert, welches Adjektiv Sie auch bevorzugen, also gibt es keinen besseren Weg? Dh. Das Einwerfen einer Aggregatfunktion wie MAX (Buchdatum) oder DISTINCT, die mir oben gesagt wurde, ist viel langsamer?
Holden
Ich würde bei der Gruppe bleiben - aber vorsichtig vorgehen, zumal Sie manuell auswählen müssen, mit welchen Feldern Sie das Objekt dekorieren möchten. Das Schreiben des Handbuchs select with group by ist ein datenbankunabhängigerer Ansatz, wenn man bedenkt, dass MSSQL (wenn Sie das Pech haben, es verwenden zu müssen) und Oracle sich in ähnlicher Weise beschweren.
Omar Qureshi
DISTINCT bedeutet nicht unbedingt langsamer.
Nr.
3

Wenn ich es richtig, in PostgreSQL erinnern müssen Sie jede Spalte fügen Sie aus der Tabelle zu holen , wo die GROUP BY - Klausel gilt auf der GROUP BY - Klausel.

Franz
quelle
2

Nicht die schönste Lösung, aber das Ändern des Gruppenparameters zur Ausgabe jeder Spalte im Modell funktioniert in PostgreSQL:

expiration = Available.find(:all,
:joins => [ :room ],
:conditions => [ "rooms.hotel_id = ? AND availables.bookdate BETWEEN ? AND ?", hostel_id, date.to_s, (date+days-1).to_s ],
:group => Available.column_names.collect{|col| "availables.#{col}"},
:order => 'availables.updated_at')
Ilia
quelle
1

Laut MySQLs "Debuking GROUP BY Myths" http://dev.mysql.com/tech-resources/articles/debunking-group-by-myths.html . SQL (Version 2003 des Standards) erfordert nicht, dass Spalten, auf die in der SELECT-Liste einer Abfrage verwiesen wird, auch in der GROUP BY-Klausel angezeigt werden.

Leonel Galán
quelle
1
Wie andere bereits betont haben, müssen sie jedoch "funktional abhängig" von Spalten sein, die sich in der Spalte befindenGROUP BY . Die Fähigkeit von MySQL zu verweisen alle nicht gruppierten Spalte ist vollständig nicht-Standard und ermöglicht es Benutzern , unlogisch und unzuverlässig Abfragen zu schreiben.
IMSoP
Es war zu dieser Zeit ein Standard, daher ist es nicht "völlig unüblich". Ich stehe auf Ihrer Seite, aber das wird unsere Meinung sein.
Leonel Galán
Zu welcher Zeit? Der verlinkte Artikel (über Wayback oder Alt-URL ) besagt, dass sowohl SQL: 1999 als auch SQL: 2003 dem GROUP BYIgnorieren von MySQL Beschränkungen auferlegen .
IMSoP
1

Verwenden Sie für andere, die nach einer Möglichkeit suchen, nach einem beliebigen Feld, einschließlich eines verbundenen Felds, in postgresql zu ordnen, eine Unterabfrage:

SELECT * FROM(
SELECT DISTINCT ON(availables.bookdate) `availables`.* 
FROM `availables` INNER JOIN `rooms` ON `rooms`.id = `availables`.room_id 
WHERE (rooms.hotel_id = 5056 
AND availables.bookdate BETWEEN '2009-11-22' AND '2009-11-24')
) AS distinct_selected
ORDER BY availables.updated_at

or arel:

subquery = SomeRecord.select("distinct on(xx.id) xx.*, jointable.order_field")
      .where("").joins(")
result = SomeRecord.select("*").from("(#{subquery.to_sql}) AS distinct_selected").order(" xx.order_field ASC, jointable.order_field ASC")
Riley
quelle