Pourquoi SQL me force-t-il à répéter tous les champs non agrégés de ma clause SELECT dans ma clause GROUP BY? [fermé]

StackOverflow https://stackoverflow.com/questions/416625

  •  03-07-2019
  •  | 
  •  

Question

Cela me perturbe depuis longtemps.

99% du temps, la clause GROUP BY est une copie exacte de la clause SELECT, moins les fonctions d'agrégation (MAX, SUM, etc.).
Cela brise le principe «Ne te répète pas».

Quand la clause GROUP BY peut-elle ne pas contenir une copie exacte de la clause SELECT moins les fonctions d'agrégat?

modifier

Je réalise que certaines implémentations vous permettent d’avoir différents champs dans GROUP BY et dans le SELECT (donc 99%, pas 100%), mais c’est sûrement une exception très mineure?
Quelqu'un peut-il expliquer ce qui est censé être retourné si vous utilisez différents champs?

Merci.

Était-ce utile?

La solution

Je suis plutôt d’accord avec vous. C’est l’un des nombreux cas où SQL devrait avoir des valeurs par défaut légèrement plus intelligentes pour nous éviter de tous les taper. Par exemple, imaginez si cela était légal:

Select ClientName, InvoiceAmount, Sum(PaymentAmount) Group By *

où " * " signifiait "tous les champs non agrégés". Si tout le monde savait que c'était comme ça que ça fonctionnait, il n'y aurait pas de confusion. Vous pouvez vous inscrire dans une liste spécifique de champs si vous voulez faire quelque chose de compliqué, mais le terme splat signifie "tous". (ce qui signifie, dans ce contexte, tous les possibles ).

Accordé, " * " signifie quelque chose de différent ici que dans la clause SELECT, alors peut-être qu'un caractère différent fonctionnerait mieux:

Select ClientName, InvoiceAmount, Sum(PaymentAmount) Group By !

Il existe quelques autres domaines comme celui où SQL n’est tout simplement pas aussi éloquent qu’il pourrait l’être. Mais à ce stade, il est probablement trop enraciné pour apporter de nombreux changements importants comme celui-ci.

Autres conseils

Comme il s’agit de deux choses différentes, vous pouvez regrouper les éléments ne figurant pas dans la clause select

EDIT:

Aussi, est-il prudent de faire cette hypothèse?

J'ai une instruction SQL

Select ClientName, InvAmt, Sum(PayAmt) as PayTot

Est-ce "correct"? pour que le serveur assume que je veux grouper par NomClient ET FactureAmount? Personnellement, je préfère (et pense que c'est plus sûr) d'avoir ce code

Select ClientName, InvAmt, Sum(PayAmt) as PayTot
Group By ClientName

renvoie une erreur en m'invitant à modifier le code en

Select ClientName, Sum(InvAmt) as InvTot, Sum(PayAmt) as PayTot
Group By ClientName

J'espère / espère que nous verrons bientôt quelque chose de plus complet; une leçon d'histoire SQL sur le sujet serait utile et informative. N'importe qui? N'importe qui? Bueller?

En attendant, je peux observer les points suivants:

SQL précède le principe DRY, du moins dans la mesure où il a été documenté dans Le programmeur pragmatique .

Toutes les bases de données ne requièrent pas la liste complète: Sybase, par exemple, exécutera des requêtes telles que

.
SELECT a, b, COUNT(*)
FROM some_table
GROUP BY a

... ce qui (au moins chaque fois que j'ai accidentellement couru un tel monstre) aboutit souvent à des jeux de données par inadvertance si énormes que des requêtes affolées s'ensuivent rapidement, priant les administrateurs de bases de données de faire rebondir le serveur. Le résultat est une sorte de produit cartésien partiel, mais je pense que la plupart des utilisateurs de Sybase ne parviennent pas à implémenter correctement le standard SQL.

Peut-être avons-nous besoin d'un formulaire abrégé - appelez-le GroupSelect

GroupSelect Field1, Field2, sum(Field3) From SomeTable Where (X = "3")

De cette façon, l'analyseur n'a qu'à envoyer une erreur si vous omettez une fonction d'agrégat.

La bonne raison à cela est que vous obtiendrez le plus souvent des résultats incorrects si vous ne spécifiez pas toutes les colonnes. Supposons que vous ayez trois colonnes, col1 , col2 et col3 .

Supposons que vos données ressemblent à ceci:

Col1  Col2 Col3
a      b    1
a      c    1
b      b    2
a      b    3

sélectionnez col1, col2, somme (col3) du groupe mytable par col1, col2
donnerait les résultats suivants:

Col1  Col2 Col3
a      b    4
a      c    1
b      b    2

Comment interpréterait-il
sélectionnez col1, col2, somme (col3) du groupe mytable par col1

Je suppose que ce serait

Col1  Col2 Col3
a      b    5
a      c    5
b      b    2

Ce sont clairement de mauvais résultats. Bien entendu, plus la requête est complexe et plus le nombre de jointures est élevé, moins il est probable que la requête renvoie des résultats corrects ou que le programmeur sache même s’ils sont incorrects.

Personnellement, je suis heureux que le groupe par nécessite les champs.

Je suis d’accord avec GROUP BY ALL, GROUP BY * ou quelque chose de similaire. Comme indiqué dans le message d'origine, vous souhaitez regrouper tous les types de colonnes / expressions non agrégées dans 99% (peut-être davantage).

Voici cependant un exemple où vous auriez besoin de colonnes GROUP BY, pour des raisons de compatibilité ascendante.

SELECT 
  MIN(COUNT(*)) min_same_combination_cnt, 
  MAX(COUNT(*)) max_same_comb_cnt, 
  AVG(COUNT(*)) avg_same_comb_cnt, 
  SUM(COUNT(*)) total_records,
  COUNT(COUNT(*)) distinct_combinations_cnt
FROM <some table>
GROUP BY <list of columns>

Cela fonctionne sous Oracle. Je l'utilise pour estimer la sélectivité des colonnes. Le groupe par est appliqué à la fonction d'agrégation interne. Ensuite, l'agrégat externe est appliqué.

Il serait bien de proposer une amélioration de la norme SQL. Je ne sais tout simplement pas comment cela fonctionne.

En fait, cela ne serait-il pas 100% du temps? Existe-t-il un cas dans lequel vous pouvez avoir une colonne (non agrégée) dans la sélection qui n'est pas dans GROUP BY?

Je n'ai pas de réponse cependant. Cela semble certainement être un moment délicat pour la langue.

Je partage le point de vue de l'opérateur, selon lequel la répétition est un peu gênante, en particulier si les champs non agrégés contiennent des instructions complexes telles que des ifs et des fonctions, entre autres. Ce serait bien s'il pouvait y avoir un raccourci dans la clause group by - au moins un alias de colonne. Faire référence aux colonnes par numéro peut être une autre option, même si celle-ci a probablement ses propres problèmes.

Il peut arriver que vous deviez extraire un identifiant de toutes les lignes groupées et la somme de leurs quantités, par exemple. Dans ce cas, vous devez les regrouper par nom et laisser les identifiants non groupés. SQLite semble fonctionner de cette façon.

Étant donné que group by donne un tuple unique pour tout un groupe de tuples, les autres attributs non group by doivent uniquement être utilisés dans la fonction d'agrégat. Si vous ajoutez un attribut non groupe par attribut dans select, alors SQL ne peut pas décider quelle valeur sélectionner dans ce groupe.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top