web-dev-qa-db-ja.com

冗長な集計関数を回避したり、列ごとにグループ化したりするための最良の方法

2つのテーブルがあるとします。

フー:

id
baz

バー:

id
foo_id
boom

したがって、Fooには多くのバーがあります。特定のFoosのセットについてバー全体の集計を計算する必要がある状況に頻繁に気付きますが、Fooのいくつかのプロパティも必要です。これを行う最も簡単な2つの方法は醜いです。

方法1:不要な集計関数

select
  foo.id,
  min(foo.baz) as baz,
  min(bar.boom) as min_boom
from
  foo
join
  bar on foo.id = bar.foo_id
group by
  foo.id;

方法2:列による不要なグループ化

select
  foo.id,
  foo.baz,
  min(bar.boom) as min_boom
from
  foo
join
  bar on foo.id = bar.foo_id
group by
  foo.id,
  foo.baz;

「id」以外にFooからの列が1つしかない場合、これはそれほど問題ではありませんが、含める必要のある列が多い場合、グループ化の効率は大幅に低下します。このようなクエリは、これらの両方の問題を回避しますが、扱いにくいようです。

select
  foo.id,
  foo.baz,
  x.min_boom
from
  foo
join
  (select
    foo_id, 
    min(boom) as min_boom
  from
    bar
  group by
    foo_id) x on x.foo_id = foo.id;

もっと良い方法はありますか?それが重要な場合、プラットフォームはPostgresです。

6
jph

idが主キーとして定義されている場合、出力に必要なすべてのfoo列によるグループ化を省略できます。 idでグループ化しています。このグループ化の特殊なケースは、現在のSQL標準に準拠しており、バージョン9.1以降の PostgreSQLマニュアル でも説明されています。

GROUP BYが存在する場合、または集約関数が存在する場合、集約関数内内を除いて、またはグループ化されていない列がグループ化された列。グループ化されていない列に対して返される可能性のある値が複数あるためです。 グループ化された列(またはそのサブセット)がグループ化されていない列を含むテーブルの主キーである場合、機能的な依存関係が存在します。

(強調が追加されました。)

したがって、foo.idがPKである場合、このクエリは有効です。

select
  foo.id,
  foo.baz,
  foo.whatever,
  min(bar.boom) as min_boom
from
  foo
join
  bar on foo.id = bar.foo_id
group by
  foo.id;
10
Andriy M

PostgreSQLのDISTINCT ONは非常に洗練されており、非常によく機能します(多くの場合、集約よりも優れています)。

select DISTINCT ON (foo.id, foo.baz)
  foo.id,
  foo.baz,
  bar.boom as min_boom
from
  foo
join
  bar on foo.id = bar.foo_id
ORDER BY
  foo.id,
  foo.baz,
  bar.boom;

または

select
  foo.id,
  foo.baz,
  x.min_boom
from
  foo
join
  (select DISTINCT ON (foo_id)
    foo_id, 
    boom as min_boom
  from
    bar
  ORDER BY
    foo_id,
    boom) x on x.foo_id = foo.id;