web-dev-qa-db-ja.com

制約の回避「列はGROUP BY句に表示されるか、集計関数で使用される必要があります」

私はPostgresを使用しています。これは、SELECT ... GROUP BYのすべての列がGROUP BY句に表示されるか、集計関数で使用される必要があるという制約を適用します。

人の車をモデル化していて、人の名前、免許番号、車の台数を調べたいとしましょう。これが SQL Fiddle としての私の例です。

次のスキーマがあります:

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

これがクエリです:

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;

私は知っています、私自身のビジネスロジックのため、その1人は1つのライセンスしか持てないので、その人に加わると、それがGROUP BYであったとしても、私は彼らのライセンス番号を見つけることができるはずです。ただし、これはGROUP BYステートメントの一部ではなく、集計関数を使用しないため、アクセスできません。

このクエリを慣用的な方法でどのように記述すればよいですか。 LATERAL JOIN、ウィンドウ関数、サブクエリについて漠然としたことを見てきましたが、この質問に答える最善の方法はわかりません。

6
Migwell

あなたは出来る GROUP BY結合を行う前に...つまり、クエリを逆にします。

これはあなたの古いクエリです

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;

これはあなたの新しいクエリです

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

その解決策は、あなたが望むほど効率的ではないかもしれません...別の潜在的な解決策は、person_idが指定されたときに自動車の数を返すplpgsql関数を記述することです。

私が選択するソリューションは、クエリの使用例によって異なります。クエリは常に(特定の人の)データの単一行を返すために使用されますか?または、レポートで多数の行を表示するために使用されますか? ?..

1
Joishi Bodio

seharusnya seperti ini(このようになっているはずです):

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

OVER (partion by <grouping column(s)>)を使用できます。これにより、Group byを使用せずにデータが集計されます。

これの唯一の欠点は、重複する_Person.id_と_license.expiry_date_がある場合、2つの結果が得られることです。ただし、_group by_の一部でなくても、他のデータを追加できます。ちょっとした考え。

0
SPC