SQL - utilisation d'alias dans Group By

143

Je suis simplement curieux de connaître la syntaxe SQL. Donc si j'ai

SELECT 
 itemName as ItemName,
 substring(itemName, 1,1) as FirstLetter,
 Count(itemName)
FROM table1
GROUP BY itemName, FirstLetter

Ce serait incorrect car

GROUP BY itemName, FirstLetter 

devrait vraiment être

GROUP BY itemName, substring(itemName, 1,1)

Mais pourquoi ne pouvons-nous pas simplement utiliser le premier pour plus de commodité?

Haoest
la source
13
qui est autorisé dans Postgresql
Michael Buen
7
MySQL le permet aussi
Kip
1
de quel rdbms parlez-vous?
Shiwangini

Réponses:

292

SQL est implémenté comme si une requête était exécutée dans l'ordre suivant:

  1. Clause FROM
  2. Clause WHERE
  3. Clause GROUP BY
  4. Clause HAVING
  5. Clause SELECT
  6. Clause ORDER BY

Pour la plupart des systèmes de bases de données relationnelles, cet ordre explique quels noms (colonnes ou alias) sont valides car ils doivent avoir été introduits à une étape précédente.

Ainsi, dans Oracle et SQL Server, vous ne pouvez pas utiliser un terme dans la clause GROUP BY que vous définissez dans la clause SELECT car le GROUP BY est exécuté avant la clause SELECT.

Il y a cependant des exceptions: MySQL et Postgres semblent avoir une intelligence supplémentaire qui le permet.

Codo
la source
3
J'aime cette explication. Bien que je ne puisse pas spéculer à quel point il est difficile de l'ajouter à un moteur en tant que sucre syntaxique.
Haoest
11
Une idée si la base de données est suffisamment intelligente pour réaliser la même expression se trouve dans les clauses SELECT et GROUP BY sans réévaluer les expressions? autrement dit, si c'est le cas GROUP BY substring(itemName, 1,1), la base de données est-elle suffisamment intelligente pour ne pas prendre le coup de performance de recalculer la sous-chaîne dans la clause SELECT?
Kip
10
Dans la clause SELECT d'une requête avec regroupement, vous n'avez accès qu'aux expressions GROUP BY et aux valeurs agrégées. Il ne s'agit donc pas d'être intelligent; il doit être mis en œuvre de cette manière pour que le groupement fonctionne. (Et c'est requis par la norme SQL). Mais même dans des cas plus triviaux (par exemple, la même expression dans la clause WHERE et la clause SELECT), les systèmes de base de données à la pointe de la technologie ne le calculeront certainement qu'une seule fois. Cette optimisation est appelée élimination des sous-expressions communes .
Codo
6
Qu'est-ce que l'ordre d'exécution a à voir avec la question? Ce n'est pas comme si le demandeur essayait de GROUP BY sur COUNT (). En fait, la requête demandée fonctionne très bien dans MySQL et probablement PostgreSQL comme indiqué dans les commentaires.
1
Pour mysql, sql_modesans inclure ONLY_FULL_GROUP_BY dans le masque de bits, l'optimiseur a une chance de fournir de meilleurs résultats avec une utilisation variée / différente de l'alias dans la HAVINGclause.
Drew le
28

Vous pouvez toujours utiliser une sous-requête pour pouvoir utiliser l'alias; Bien sûr, vérifiez les performances (il est possible que le serveur de base de données exécute les deux de la même manière, mais ne fait jamais de mal à vérifier):

SELECT ItemName, FirstLetter, COUNT(ItemName)
FROM (
    SELECT ItemName, SUBSTRING(ItemName, 1, 1) AS FirstLetter
    FROM table1
    ) ItemNames
GROUP BY ItemName, FirstLetter
Chris Shaffer
la source
2
Les sous-requêtes doivent être évitées autant que possible en raison de mauvaises performances. L'utilisation d'une copie de la fonction est bien meilleure car elle est bien sûr détectée par l'optimiseur de base de données et effectuée une seule fois.
Roland
1
@Roland mais il n'y a pas de différence dans le plan d'exécution dans ce cas. Y a-t-il une autre considération de performance?
Guido Mocha
@Roland, les sous-requêtes corrélées ou toute autre syntaxe qui conduit à des boucles ou à un comportement ligne par ligne doivent être évitées, et il y a une limite à la profondeur à laquelle vous devez aller avec les sous-requêtes imbriquées, mais il n'est généralement pas vrai que les sous-requêtes conduisent à de mauvaises performances. Dans ce cas, comme Chris l'a dit, vous pouvez vérifier le plan d'exécution (plan de requête AKA, plan d'explication) en comparant à la fois avec et sans la sous-requête, et voir s'il y a vraiment une différence. Presque tous les moteurs de base de données réécriront votre requête afin que vous ne contrôliez pas totalement ce qui est exécuté. C'est le but de la syntaxe déclarative.
Davos
16

Au moins dans PostgreSQL, vous pouvez utiliser le numéro de colonne dans le jeu de résultats dans votre clause GROUP BY:

SELECT 
 itemName as ItemName,
 substring(itemName, 1,1) as FirstLetter,
 Count(itemName)
FROM table1
GROUP BY 1, 2

Bien sûr, cela commence à être pénible si vous faites cela de manière interactive et que vous modifiez la requête pour changer le nombre ou l'ordre des colonnes dans le résultat. Mais reste.

Bill Gribble
la source
GROUP BY FirstLetterest autorisé dans Postgresql. Pour savoir, essayez d'exécuter ceci dans Postgresql: sélectionnez la sous-chaîne (nom_table, 1,2) comme tname à partir du groupe information_schema.tables par tname
Michael Buen
1
@MichaelBuen me semble potentiellement problématique. D'un test rapide, il semble que s'il y a un alias et une colonne de table de base avec le même nom, cette dernière obtient la priorité? SQL Fiddle . Donc, si le fait de s'appuyer sur ce groupe par alias, un changement de schéma ultérieur pourrait interrompre silencieusement votre requête et changer la sémantique.
Martin Smith
@MartinSmith savait seulement maintenant que c'était un piège, s'abstiendra de l'utiliser, merci. Étant donné que PostgreSQL autorise ce raccourci, ils devraient donner une priorité à l'alias, sinon ils ne devraient pas autoriser ce raccourci du tout.
Michael Buen
C'était une idée terrible des concepteurs de PostgreSQL. C'est déroutant dès que vous essayez GROUP BYune expression qui contient des fonctions d'agrégation ou des fonctions de fenêtre, ce qui "évidemment" ne fonctionne pas.
Lukas Eder
13

SQL Server ne vous permet pas de référencer l'alias dans la clause GROUP BY en raison de l'ordre logique de traitement. La clause GROUP BY est traitée avant la clause SELECT, de sorte que l'alias n'est pas connu lorsque la clause GROUP BY est évaluée. Cela explique également pourquoi vous pouvez utiliser l'alias dans la clause ORDER BY.

Voici une source d'informations sur les phases de traitement logique de SQL Server .

bobs
la source
8

Je ne réponds pas pourquoi il en est ainsi, mais je voulais seulement montrer un moyen de contourner cette limitation dans SQL Server en utilisant CROSS APPLYpour créer l'alias. Vous l'utilisez ensuite dans la GROUP BYclause, comme ceci:

SELECT 
 itemName as ItemName,
 FirstLetter,
 Count(itemName)
FROM table1
CROSS APPLY (SELECT substring(itemName, 1,1) as FirstLetter) Alias
GROUP BY itemName, FirstLetter
Ricardo
la source
4

Attention, l'utilisation d'alias dans Group By (pour les services qui le prennent en charge, comme postgres) peut avoir des résultats inattendus. Par exemple, si vous créez un alias qui existe déjà dans l'instruction interne, le Group By choisira le nom du champ interne.

-- Working example in postgres
select col1 as col1_1, avg(col3) as col2_1
from
    (select gender as col1, maritalstatus as col2, 
    yearlyincome as col3 from customer) as layer_1
group by col1_1;

-- Failing example in postgres
select col2 as col1, avg(col3)
from
    (select gender as col1, maritalstatus as col2,
    yearlyincome as col3 from customer) as layer_1
group by col1;
Shannon S
la source
3

Certains SGBD vous permettront d'utiliser un alias au lieu d'avoir à répéter l'expression entière.
Teradata en est un exemple.

J'évite la notation de position ordinale comme recommandé par Bill pour des raisons documentées dans cette question SO .

L'alternative simple et robuste consiste à toujours répéter l'expression dans la clause GROUP BY.
DRY ne s'applique PAS à SQL.

viande_mécanique
la source
1

Méfiez-vous des alias lors du regroupement des résultats d'une vue dans SQLite. Vous obtiendrez des résultats inattendus si le nom d'alias est le même que le nom de colonne de toutes les tables sous-jacentes (pour les vues.)

GGGforce
la source
0

À l'époque, j'ai découvert que Rdb, l'ancien produit DEC maintenant pris en charge par Oracle, permettait d'utiliser l'alias de colonne dans le GROUP BY. Oracle Mainstream jusqu'à la version 11 n'autorise pas l'utilisation de l'alias de colonne dans GROUP BY. Je ne sais pas ce que Postgresql, SQL Server, MySQL, etc. autoriseront ou non. YMMV.

Bob Jarvis - Réintégrer Monica
la source