2010-10-01 3 views
88

Solo curiosità sulla sintassi SQL. Quindi, se hoSQL: utilizzo di alias nel gruppo

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

Questo sarebbe corretto perché

GROUP BY itemName, FirstLetter 

in realtà dovrebbe essere

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

Ma perché non possiamo semplicemente utilizzare il primo per convenienza?

+5

che è permesso in PostgreSQL –

+3

MySQL permette anche – Kip

+0

SQLite permette –

risposta

161

SQL è implementato come se una query è stata eseguita nel seguente ordine:

  1. clausola FROM
  2. clausola WHERE
  3. clausola GROUP BY
  4. HAVING
  5. SELEZIONA clausola
  6. ORDER BY clausola

Per la maggior parte dei sistemi di database relazionali, questo ordine spiega quali nomi (colonne o alias) sono validi perché devono essere stati introdotti in un passaggio precedente.

Pertanto, in Oracle e SQL Server, non è possibile utilizzare un termine nella clausola GROUP BY definita nella clausola SELECT perché GROUP BY viene eseguito prima della clausola SELECT.

Ci sono tuttavia delle eccezioni: MySQL e Postgres sembrano avere un'ulteriore intelligenza che lo consenta.

+0

Mi piace questa spiegazione. Anche se non posso speculare quanto sia difficile aggiungerlo a un motore come zucchero sintattico. – Haoest

+8

Qualche idea se il DB è abbastanza intelligente da realizzare la stessa espressione è nelle clausole SELECT e GROUP BY senza rivalutare le espressioni? Ad esempio, se esiste una sottostringa 'GROUP BY (itemName, 1,1)', il database è abbastanza intelligente da non subire il colpo di prestazioni di ricalcolo della sottostringa nella clausola SELECT? – Kip

+8

Nella clausola SELECT di una query con raggruppamento, si ha accesso solo alle espressioni GROUP BY e ai valori aggregati. Quindi non si tratta di essere intelligenti; deve essere implementato in questo modo affinché il gruppo funzioni. (Ed è richiesto dallo standard SQL). Ma anche in casi più banali (ad esempio la stessa espressione nel WHERE e nella clausola SELECT), i sistemi di database all'avanguardia lo calcolano solo una volta. Questa ottimizzazione è chiamata * eliminazione di sottoespressione comune *. – Codo

11

Almeno in PostgreSQL è possibile utilizzare il numero di colonna nel set di risultati nella clausola GROUP BY:

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

Naturalmente questo comincia ad essere un dolore se si sta facendo questo in modo interattivo e modificare la query per cambia il numero o l'ordine delle colonne nel risultato. Ma ancora.

+0

'GROUP BY FirstLetter' è consentito in PostgreSQL. Per provare, prova a farlo in Postgresql: seleziona sottostringa (nome_tabella, 1,2) come tname da information_schema.tables gruppo per tname –

+1

@MichaelBuen Sembra potenzialmente problematico per me. Da un rapido test sembra che ci sia un alias e una colonna di una tabella di base con lo stesso nome che quest'ultimo ha la priorità? [SQL Fiddle] (http://sqlfiddle.com/#!15/d41d8/1920). Quindi, se affidarsi a questo gruppo di alias, una modifica dello schema successiva potrebbe interrompere in modo silenzioso la query e modificare la semantica. –

+0

@MartinSmith sapeva solo ora che è un trucco, si asterrà dall'utilizzarlo, grazie. Dato che PostgreSQL consente questa scorciatoia, dovrebbero dare l'alias una priorità, altrimenti non dovrebbero permettere affatto quel collegamento. –

2

Alcuni DBMS consentono di utilizzare un alias anziché dover ripetere l'intera espressione.
Teradata è uno di questi esempi.

Evito la notazione di posizione ordinale come raccomandato da Bill per i motivi documentati in this SO question.

L'alternativa semplice e affidabile consiste nel ripetere sempre l'espressione nella clausola GROUP BY.
DRY non si applica a SQL.

22

È sempre possibile utilizzare una sottoquery in modo da poter utilizzare l'alias; Naturalmente, verificare le prestazioni (Possibile il db server verrà eseguito sia lo stesso, ma non fa mai male a verificare):

SELECT ItemName, FirstLetter, COUNT(ItemName) 
FROM (
    SELECT ItemName, SUBSTRING(ItemName, 1, 1) AS FirstLetter 
    FROM table1 
    ) ItemNames 
GROUP BY ItemName, FirstLetter 
+0

grazie, buona idea! – digz6666

0

Indietro nel giorno in cui ho scoperto che Rdb, l'ex prodotto di dicembre ora supportato da Oracle ha permesso la l'alias di colonna da utilizzare in GROUP BY. Oracle principale attraverso la versione 11 non consente l'utilizzo dell'alias di colonna in GROUP BY. Non sono sicuro di ciò che Postgresql, SQL Server, MySQL, ecc. Permetteranno o non permetteranno. YMMV.

8

SQL Server non consente di fare riferimento all'alias nella clausola GROUP BY a causa dell'ordine logico di elaborazione. La clausola GROUP BY viene elaborata prima della clausola SELECT, quindi l'alias non è noto quando viene valutata la clausola GROUP BY. Questo spiega anche perché è possibile utilizzare l'alias nella clausola ORDER BY.

Ecco una fonte per informazioni su SQL Server logical processing phases.

1

Fare attenzione all'utilizzo di alias quando si raggruppano i risultati da una vista in SQLite. Otterrai risultati imprevisti se il nome dell'alias è uguale al nome della colonna di qualsiasi tabella sottostante (alle viste.)

3

Attenzione che l'utilizzo di alias nel gruppo Per (per i servizi che lo supportano, come postgres) può avere risultati non intenzionali Ad esempio, se si crea un alias già esistente nell'istruzione interna, Group By sceglierà il nome del 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; 
0

Non sto rispondendo perché è così, ma solo voluto mostrare un modo per aggirare questa limitazione in SQL Server utilizzando CROSS APPLY per creare l'alias. È quindi utilizzarlo nella clausola GROUP BY, in questo modo:

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