Rails erweitert Felder mit Geltungsbereich, PG mag es nicht

8

Ich habe ein Modell von Widgets. Widgets gehören zu einem Store-Modell, das zu einem Area-Modell gehört, das zu einem Unternehmen gehört. Beim Unternehmensmodell muss ich alle zugehörigen Widgets finden. Einfach:

%Vor%

Was wird diese schöne Abfrage erzeugen:

%Vor%

Aber später muss ich diesen Bereich in einem größeren Umfang verwenden. Das Problem besteht darin, dass AR die Abfrage erweitert, indem einzelne Felder ausgewählt werden, was in PG fehlschlägt, da ausgewählte Felder in der GROUP BY-Klausel oder der Aggregatfunktion enthalten sein müssen.

Hier ist der komplexere Bereich.

%Vor%

Und das ist die Auswahl, die auf PG geworfen wird:

%Vor%

Was diesen Fehler erzeugt:

  

PGError: Fehler: Spalte   "widgets.id" muss in der erscheinen   GROUP BY-Klausel oder in einem verwendet werden   Aggregatfunktion LINE 1: SELECT   "Widgets". "ID" AS t0_r0,   "Widgets". "user_id ...

Wie schreibe ich den Widget.in_company-Bereich neu, so dass AR die Auswahlabfrage nicht so erweitert, dass sie jedes Widget-Modellfeld enthält?

    
Karl 25.04.2011, 18:27
quelle

5 Antworten

10

Wie Frank erklärte, wird PostgreSQL jede Anfrage ablehnen, die keine reproduzierbaren Zeilen zurückgibt.

Angenommen, Sie haben eine Abfrage wie:

%Vor%

PostgreSQL lehnt es ab, weil b in der group by -Anweisung nicht angegeben ist. Führen Sie das in MySQL dagegen aus, und es wird akzeptiert. Im letzteren Fall werden jedoch einige Einfügungen, Aktualisierungen und Löschungen ausgelöst, und die Reihenfolge der Zeilen auf den Plattenseiten endet anders.

Wenn der Speicher dient, sind die Implementierungsdetails so, dass MySQL tatsächlich nach a, b sortiert und das erste b in der Menge zurückgibt. Aber was den SQL-Standard betrifft, ist das Verhalten unspezifiziert - und tatsächlich sortiert PostgreSQL nicht immer vor dem Ausführen von Aggregatfunktionen.

Dies kann möglicherweise zu unterschiedlichen Werten von b im Ergebnis in PostgreSQL führen. Und so liefert PostgreSQL einen Fehler, wenn Sie nicht genauer sind:

%Vor%

Was Frank hervorgehoben hat, ist, dass, wenn a der Primärschlüssel ist, in PostgreSQL 9.1 b unspezifiziert bleiben kann - der Planer hat gelernt, nachfolgende Gruppen von Feldern zu ignorieren, wenn die Primärschlüssel eine eindeutige Bedeutung haben Reihe.

Für Ihr Problem müssen Sie Ihre Gruppe genau wie bisher festlegen, plus jedes Feld, auf dem Sie Ihr Aggregat aufbauen, also "widgets"."id", "widgets"."user_id", [snip] , aber nicht wie sum(amount) , welche die aggregierten Funktionsaufrufe sind.

Als Nebenbemerkung bin ich mir nicht sicher, wie Ihr ORM / Modell funktioniert, aber die SQL, die es generiert, ist nicht optimal. Viele dieser linken Outer-Joins scheinen wie innere Joins zu sein. Dies wird dazu führen, dass der Planer gegebenenfalls eine passende Join-Reihenfolge auswählen kann.

    
Denis de Bernardy 24.05.2011 05:45
quelle
3

PostgreSQL Version 9.1 ( Beta in diesem Moment ) könnte Ihr Problem beheben, aber nur, wenn es eine Funktion gibt Abhängigkeit vom Primärschlüssel.

Aus den Versionshinweisen:

  

Erlaube Nicht-GROUP BY-Spalten in der   Zielliste beim Primärschlüssel abfragen   ist in der GROUP BY-Klausel angegeben   (Peter Eisentraut)

     

Ein anderes Datenbanksystem bereits   erlaubt dieses Verhalten und wegen   der Primärschlüssel ist das Ergebnis   eindeutig.

Sie könnten einen Test durchführen und sehen, ob er Ihr Problem behebt. Wenn Sie auf die Produktionsversion warten können, kann dies das Problem beheben, ohne den Code zu ändern.

    
Frank Heikens 18.05.2011 07:46
quelle
2

Vereinfachen Sie zunächst Ihr Leben, indem Sie alle Daten in einer Standard-Zeitzone speichern. Das Ändern von Daten mit Zeitzonen sollte wirklich in der Ansicht als Benutzerfreundlichkeit erfolgen. Dies allein sollte dir eine Menge Schmerzen ersparen.

Wenn Sie bereits in Produktion sind, schreiben Sie eine Migration, um eine Spalte normalised_date zu erstellen, wo immer es hilfreich wäre.

nrIch schlage vor, dass das andere Problem hier die Verwendung von Raw SQL ist, welche Rails nicht für Sie herumstochern. Um dies zu vermeiden, solltest du das Juwel namens Squeel (alias Metawhere 2) Ссылка

verwenden

Wenn Sie dies verwenden, sollten Sie in der Lage sein, hartcodiertes SQL zu entfernen und Rails wieder in die Magie zu versetzen.

Zum Beispiel:

%Vor%

wird (sobald Sie das Datum nicht mehr normalisieren):

%Vor%     
thomasfedb 23.05.2011 12:12
quelle
0

Tut mir leid, meine eigene Frage zu beantworten, aber ich habe es herausgefunden.

Zuerst möchte ich mich bei denen entschuldigen, die dachten, ich könnte ein SQL- oder Postgres-Problem haben, das ist es nicht. Das Problem tritt mit ActiveRecord und dem SQL auf, das es generiert.

Die Antwort lautet ... Verwenden Sie .joins anstelle von . includes . Also habe ich gerade die Zeile im Top-Code geändert und es funktioniert wie erwartet.

%Vor%

Ich vermute, dass ActiveRecord bei der Verwendung von .includs versucht, intelligent zu sein und JOINS in SQL zu verwenden, aber es ist nicht intelligent genug für diesen speziellen Fall und erzeugt dieses hässliche SQL, um alle zugehörigen Spalten auszuwählen.

Aber alle Antworten haben mir einiges über Postgres beigebracht, das ich nicht kannte, also vielen Dank.

    
Karl 27.05.2011 18:47
quelle
0

Sortierung in mysql:

%Vor%

in postgres:

%Vor%     
ilgam 05.11.2014 16:44
quelle