web-dev-qa-db-fra.com

Meilleur moyen d'éviter les fonctions d'agrégats redondants et / ou au groupe par colonnes

Supposons que j'ai deux tables:

foo :

id
baz

barre :

id
foo_id
boom

Donc, un foo a beaucoup de barres. Je me trouve souvent dans des situations où j'ai besoin de calculer un agrégat dans les barres pour un ensemble donné de Foos, mais je veux aussi des propriétés de FOO. Les deux façons les plus simples de faire cela sont laids:

Méthode n ° 1: fonctions d'agrégat inutiles

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;

Méthode n ° 2: groupe inutile par colonne

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;

Ce n'est pas si mauvais quand il n'y a qu'une seule colonne supplémentaire de FOO à part "ID", mais s'il y a beaucoup de colonnes à inclure, le groupement devient beaucoup moins efficace. Une requête comme celle-ci se déplace à la fois ces problèmes, mais semble lourd:

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;

Y a-t-il une meilleure façon? La plate-forme est postgres si cela importe.

6
jph

Si ID est défini comme clé primaire, vous pouvez omettre le regroupement par tous les colonnes FOO Colonnes que vous souhaitez pour la sortie tant que vous regroupez par le = ID. Ce cas particulier de regroupement est conforme à la norme SQL actuelle et a également été traitée dans le Manuel PostgreSQL , à partir de la version 9.1:

Lorsque le groupe par est présent ou toutes les fonctions globales sont présentes, il n'est pas valide pour les expressions de liste de sélection de sélectionner des colonnes non groupées, à l'exception des fonctions globales ou lorsque la colonne non groupée dépend de la fonction de regroupement , comme il serait autrement plus d'une valeur possible pour revenir à une colonne non groupée. ne dépendance fonctionnelle existe si les colonnes regroupées (ou un sous-ensemble) sont la clé primaire du tableau contenant la colonne non groupée.

((emphase).)

Donc, si FOO.ID est le PK, cette requête serait valide:

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

Le distinct de postgreSQL est très élégant et se produit très bien (souvent mieux que les agrégats):

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;

Ou alors

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;