it-swarm.com.de

Umgehen der Einschränkung "Die Spalte muss in der GROUP BY-Klausel erscheinen oder in einer Aggregatfunktion verwendet werden."

Ich verwende Postgres, wodurch die Einschränkung erzwungen wird, dass alle Spalten in einer SELECT ... GROUP BY-Klausel in der GROUP BY-Klausel erscheinen oder in einer Aggregatfunktion verwendet werden müssen.

Nehmen wir an, ich modelliere die Autos von Menschen und möchte den Namen, die Lizenznummer und die Anzahl der Autos einer Person herausfinden. Hier ist mein Beispiel als SQL Fiddle .

Ich hätte das folgende Schema:

CREATE TABLE person(
  id integer PRIMARY KEY,
  name text
);

CREATE TABLE license(
  person_id integer REFERENCES person(id),
  expiry_date date
);

CREATE TABLE car(
  owner_id integer REFERENCES person(id),
  registration_number TEXT
);

Hier ist die Abfrage:

SELECT person.name, person.id, license.expiry_date, COUNT(car) FROM person
  JOIN license ON license.person_id = person.id
  JOIN car ON car.owner_id = person.id
WHERE person.name = 'Charles Bannerman'
GROUP BY person.id;

Ich weiß, aufgrund meiner eigenen Geschäftslogik, dass eine Person nur eine Lizenz haben kann , also wenn ich beitrete für die Person, obwohl es GROUP BY'd ist, sollte ich in der Lage sein, ihre Lizenznummer zu finden. Da es jedoch nicht Teil der GROUP BY-Anweisung ist und keine Aggregatfunktion verwendet, kann ich nicht darauf zugreifen.

Wie soll ich diese Abfrage idiomatisch schreiben? Ich habe vage Dinge über LATERAL JOINs, Fensterfunktionen und Unterabfragen gesehen, bin mir aber nicht sicher, wie ich diese Frage am besten beantworten kann.

6
Migwell

Du könntest GROUP BY bevor Sie beitreten ... Das heißt, kehren Sie Ihre Abfrage um.

Dies ist Ihre ALTE Abfrage

SELECT person.name, person.id, license.expiry_date, COUNT(car) FROM person
  JOIN license ON license.person_id = person.id
  JOIN car ON car.owner_id = person.id
WHERE person.name = 'Charles Bannerman'
GROUP BY person.id;

Dies ist Ihre NEUE Anfrage

WITH vals AS (
  SELECT owner_id, COUNT(car) AS cars FROM car GROUP BY owner_id
) SELECT
  person.name, person.id, license.expiry_date, vals.cars
FROM cars
JOIN person ON cars.owner_id = person.id
JOIN license ON person.id = license.person_id
WHERE person.name = 'Charles Bannerman';

Diese Lösung ist möglicherweise nicht so effizient wie Sie möchten ... Eine andere mögliche Lösung wäre, eine plpgsql-Funktion zu schreiben, um die Anzahl der Autos zurückzugeben, wenn eine person_id angegeben wird.

Die Lösung, die ich wählen würde, hängt vom Anwendungsfall der Abfrage ab. Wird die Abfrage IMMER verwendet, um eine einzelne Datenzeile (für eine bestimmte Person) zurückzugeben? Oder wird sie in einem Bericht verwendet, um viele Zeilen anzuzeigen ? ..

1
Joishi Bodio

seharusnya seperti ini (soll so sein):

SELECT person.name, person.id, license.expiry_date, COUNT(car) FROM person
  JOIN license ON license.person_id = person.id
  JOIN car ON car.owner_id = person.id
WHERE person.name = 'Charles Bannerman'
GROUP BY person.name, person.id, license.expiry_date, car.car;
1
Hamba
SELECT person.name, 
       person.id, 
       license.expiry_date, COUNT(car) OVER (Partition by Person.Id)
FROM person
  JOIN license ON license.person_id = person.id
  JOIN car ON car.owner_id = person.id
WHERE person.name = 'Charles Bannerman';

Sie können OVER (partion by <grouping column(s)>) verwenden. Dadurch werden die Daten aggregiert, ohne dass eine Gruppe von verwendet werden muss.

Der einzige Nachteil dabei ist, dass bei doppelten Person.id Und license.expiry_date 2 Ergebnisse erzielt werden. Sie können jedoch auch andere Daten hinzufügen, ohne dass diese Teil des group by Sind. Nur ein Gedanke.

0
SPC