SQL - usando alias no grupo por

143

Apenas curioso sobre a sintaxe SQL. Então, se eu tiver

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

Isso estaria incorreto porque

GROUP BY itemName, FirstLetter 

realmente deveria ser

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

Mas por que não podemos simplesmente usar o primeiro por conveniência?

Haoest
fonte
13
permitido no Postgresql
Michael Buen
7
O MySQL também permite
Kip
1
de que rdbms você está falando?
Shiwangini 22/04/19

Respostas:

292

SQL é implementado como se uma consulta fosse executada na seguinte ordem:

  1. Cláusula FROM
  2. Cláusula WHERE
  3. Cláusula GROUP BY
  4. Cláusula HAVING
  5. Cláusula SELECT
  6. Cláusula ORDER BY

Para a maioria dos sistemas de bancos de dados relacionais, essa ordem explica quais nomes (colunas ou aliases) são válidos porque devem ter sido introduzidos em uma etapa anterior.

Portanto, no Oracle e no SQL Server, você não pode usar um termo na cláusula GROUP BY que você define na cláusula SELECT porque o GROUP BY é executado antes da cláusula SELECT.

Porém, existem exceções: MySQL e Postgres parecem ter inteligência adicional que permite isso.

Codo
fonte
3
Eu gosto dessa explicação. Embora eu não possa especular o quão difícil é adicioná-lo a um mecanismo como açúcar sintático.
Mai10
11
Alguma idéia se o banco de dados é inteligente o suficiente para perceber a mesma expressão está nas cláusulas SELECT e GROUP BY sem reavaliar as expressões? ou seja, se houver GROUP BY substring(itemName, 1,1), o banco de dados é inteligente o suficiente para não sofrer o desempenho de recalcular a substring na cláusula SELECT?
Kip
10
Na cláusula SELECT de uma consulta com agrupamento, você só tem acesso às expressões GROUP BY e aos valores agregados. Portanto, não se trata de ser inteligente; deve ser implementado dessa maneira para o agrupamento funcionar. (E é exigido pelo padrão SQL). Mas mesmo em casos mais triviais (por exemplo, a mesma expressão na cláusula WHERE e SELECT), os sistemas de banco de dados de ponta certamente o computarão apenas uma vez. Essa otimização é chamada eliminação comum de subexpressão .
Codo
6
O que a ordem de execução tem a ver com a pergunta? Não é como se o autor da pergunta estivesse tentando GROUP BY em COUNT (). De fato, a consulta solicitada funciona muito bem no MySQL e provavelmente no PostgreSQL, como apontado nos comentários.
1
Para o mysql, sql_modenão incluindo ONLY_FULL_GROUP_BY na máscara de bit , o Optimizer tem a chance de oferecer melhores resultados com um uso variado / diferente do alias na HAVINGcláusula.
Tirou
28

Você sempre pode usar uma subconsulta para poder usar o alias; Obviamente, verifique o desempenho (é possível que o servidor db funcione da mesma forma, mas nunca é demais verificar):

SELECT ItemName, FirstLetter, COUNT(ItemName)
FROM (
    SELECT ItemName, SUBSTRING(ItemName, 1, 1) AS FirstLetter
    FROM table1
    ) ItemNames
GROUP BY ItemName, FirstLetter
Chris Shaffer
fonte
2
As subconsultas devem ser evitadas sempre que possível devido ao mau desempenho. O uso de uma cópia da função é muito melhor porque é claro que é detectado pelo otimizador de banco de dados e feito apenas uma vez.
Roland
1
@Roland, mas não há diferenças no plano de execução nesse caso. Há alguma outra consideração de desempenho?
Guido Mocha
@Roland, subconsultas correlacionadas ou outras sintaxes que levam a loops ou comportamento linha a linha devem ser evitadas, e há um limite de quão profundo você deve ir com subconsultas aninhadas, mas geralmente não é verdade que as subconsultas levam para um desempenho ruim. Nesse caso, como Chris disse, você pode verificar o plano de execução (plano de consulta AKA, plano de explicação) comparando com e sem a subconsulta e ver se há realmente alguma diferença. Praticamente todos os mecanismos de banco de dados reescrevem sua consulta para que você não esteja totalmente no controle do que é executado. Esse é o ponto da sintaxe declarativa.
Davos
16

Pelo menos no PostgreSQL, você pode usar o número da coluna no conjunto de resultados na sua cláusula GROUP BY:

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

É claro que isso começa a ser problemático se você estiver fazendo isso de maneira interativa e editar a consulta para alterar o número ou a ordem das colunas no resultado. Mas ainda.

Bill Gribble
fonte
GROUP BY FirstLetteré permitido no Postgresql. A saber, tente executar este no PostgreSQL: select substring (table_name, 1,2) como tname do grupo information_schema.tables por tname
Michael Buen
1
@MichaelBuen Parece potencialmente problemático para mim. A partir de um teste rápido, parece que existe um alias e uma coluna da tabela base com o mesmo nome, que este recebe prioridade? SQL Fiddle . Portanto, se depender desse grupo como alias, uma alteração posterior do esquema poderá interromper silenciosamente sua consulta e alterar a semântica.
Martin Smith
A @MartinSmith só sabia agora que é uma pegadinha, vai se abster de usá-la, obrigado. Dado que o PostgreSQL permite esse atalho, eles devem dar uma prioridade ao alias, caso contrário, eles não devem permitir esse atalho.
Michael Buen
Essa foi uma péssima idéia dos designers do PostgreSQL. É confuso assim que você tenta GROUP BYqualquer expressão que contenha funções agregadas ou funções da janela, o que "obviamente" não funciona.
Lukas Eder
13

O SQL Server não permite que você faça referência ao alias na cláusula GROUP BY devido à ordem lógica do processamento. A cláusula GROUP BY é processada antes da cláusula SELECT, portanto, o alias não é conhecido quando a cláusula GROUP BY é avaliada. Isso também explica por que você pode usar o alias na cláusula ORDER BY.

Aqui está uma fonte de informações sobre as fases do processamento lógico do SQL Server .

bobs
fonte
8

Não estou respondendo por que é assim, mas só queria mostrar uma maneira de contornar essa limitação no SQL Server usando CROSS APPLYpara criar o alias. Você o usa na GROUP BYcláusula, assim:

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

Cuidado para que o uso do alias no Group By (para serviços que o suportem, como o postgres) possa ter resultados indesejados. Por exemplo, se você criar um alias que já existe na instrução interna, o Agrupar por escolherá o nome do campo interno.

-- 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
fonte
3

Alguns DBMSs permitem que você use um alias em vez de precisar repetir a expressão inteira.
Teradata é um exemplo.

Evito a notação da posição ordinal, conforme recomendado por Bill, pelos motivos documentados nesta pergunta do SO .

A alternativa fácil e robusta é sempre repetir a expressão na cláusula GROUP BY.
DRY NÃO se aplica ao SQL.

carne_mecânica
fonte
1

Cuidado ao usar aliases ao agrupar os resultados de uma exibição no SQLite. Você obterá resultados inesperados se o nome alternativo for o mesmo que o nome da coluna de qualquer tabela subjacente (para as visualizações).

GGGforce
fonte
0

Naquele dia, descobri que o Rdb, o antigo produto DEC agora suportado pela Oracle, permitia que o alias da coluna fosse usado no GROUP BY. O mainstream Oracle através da versão 11 não permite que o alias da coluna seja usado no GROUP BY. Não tenho certeza do que o Postgresql, SQL Server, MySQL, etc permitirá ou não. YMMV.

Bob Jarvis - Restabelecer Monica
fonte