Perché MySQL consente ad HAVING di utilizzare gli alias SELECT?


14

In SQL, per quanto ne so, l'ordine di elaborazione della query logica, che è l'ordine di interpretazione concettuale, inizia con FROM nel modo seguente:

  1. A PARTIRE DAL
  2. DOVE
  3. RAGGRUPPARE PER
  4. VISTA
  5. SELEZIONARE
  6. ORDINATO DA

Seguendo questo elenco è facile capire perché non è possibile avere alias SELECT in una clausola WHERE, poiché l'alias non è stato ancora creato. T-SQL (SQL Server) segue questo rigorosamente e non è possibile utilizzare gli alias SELECT fino a quando non si supera SELECT.

Ma in MySQL è possibile utilizzare gli alias SELECT nella clausola HAVING anche se dovrebbe (logicamente) essere elaborato prima della clausola SELECT. Come può essere possibile?

Per fare un esempio:

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;

L'istruzione non è valida in T-SQL (poiché HAVING si riferisce all'alias SELECT Amount) ...

Msg 207, Level 16, State 1, Line 5
Invalid column name 'Amount'.

... ma funziona benissimo in MySQL.

Sulla base di questo, mi chiedo:

  • MySQL sta prendendo un collegamento nelle regole SQL per aiutare l'utente? Forse usando una sorta di pre-analisi?
  • O MySQL sta usando un ordine di interpretazione concettuale diverso da quello che tutti i RDBMS stavano seguendo?

1
Suppongo sia il tuo secondo punto elenco.
a_horse_with_no_name

3
Beh, suppongo che non causi alcuna ambiguità o confusione fino a quando non supportano le funzioni di classifica. Quindi SELECT C, ROW_NUMBER() OVER (ORDER BY X) AS RN FROM T GROUP BY C HAVING RN = 1sarà problematico in quanto le ROW_NUMBERcorse dopo ilHAVING
Martin Smith

Non sono sicuro di quali funzioni di classificazione sono supportate da MySQL. Se si desidera che il numero di riga è necessario creare in questo modo: SELECT @rownum:=@rownum + 1 as row .... Forse il motivo per cui supportano semplicemente gli alias SELECT è perché possono, a causa del fatto che non supportano cose che renderebbero impossibile ... chi lo sa? :)
Ohlin,

Come spiega @MartinSmith, fintanto che non ci sono funzioni finestra / classifica, l'ordine logico di esecuzione per HAVINGe la SELECTclausola possono essere scambiati. Quindi, non c'è ambiguità nel fare questo e può semplificare l'aspetto del codice quando ci sono espressioni mostruose in SELECT.
ypercubeᵀᴹ

Spero che questo sia un po 'in argomento per dire che ho risposto a una domanda Qui che sta godendo risultati più veloci (con distincts) ... con Alias in the Havinglo stesso Explainrisultato. Quindi si stanno verificando alcune variazioni con l'ottimizzatore.
Estratto il

Risposte:


13

Bene, quando hai una domanda di questo tipo, la migliore fonte di informazioni IMHO è la documentazione di MySQL. Adesso al punto. Questo è il comportamento dell'estensione MySql a GROUP BYcui è abilitato per impostazione predefinita.

MySQL
estende a GROUP BY MySQL estende questo comportamento per consentire l'uso di un alias nella clausola HAVING per la colonna aggregata

Se si desidera un comportamento standard, è possibile disabilitare questa estensione con sql_mode ONLY_FULL_GROUP_BY

SET [SESSION | GLOBAL] sql_mode = ONLY_FULL_GROUP_BY;

Se si tenta di eseguire la query sopra menzionata in ONLY_FULL_GROUP_BYsql_mode, verrà visualizzato il seguente messaggio di errore:

Il campo non raggruppante 'Importo' viene utilizzato nella clausola HAVING: SELEZIONA ANNO (data ordine), COUNT (*) come Importo DA Ordini GRUPPO PER ANNO (data ordine) HAVING Importo> 1

Ecco la demo di SQLFiddle

Pertanto dipende da te come configurare e utilizzare la tua istanza di MySQL.


Hai assolutamente ragione sulla documentazione. Non avrei mai pensato che potesse essere scritto in modo così chiaro come lo hai citato sopra :) Grazie per averlo trovato ...
Ohlin,

Questa risposta non risponde "MySQL sta eseguendo la pre-analisi o MySQL sta utilizzando un'interpretazione concettuale diversa?".
Pacerier,

2
@Pacerier MySQL sta "facendo pre-analisi", ovviamente, perché Query Optimizer considera tutti gli aspetti della query mentre sceglie quello che ritiene sarà il miglior piano di query. L'idea di una "diversa interpretazione concettuale" tradisce un malinteso sul fatto che il server è libero di implementare il modello concettuale in qualsiasi modo che produca un risultato valido. ORDER BY, ad esempio, potrebbe essere effettivamente gestito molto prima di quanto teoricamente, se l'ottimizzatore rileva che le righe possono essere inizialmente lette in ordine da un indice che è già nell'ordine desiderato.
Michael - sqlbot,

4

Buona domanda.

Penso che dovresti eseguire queste query

EXPLAIN SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;
SHOW WARNINGS;

e controlla come viene riscritta la query. sono abbastanza sicuro che Query Optimizer sostituisca Amount con COUNT (*)

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING COUNT(*)>1;

Come fa con

select 
 *
from 
 test
where 
 id = 5 - 3

dopo Query Optimizer è qualcosa del genere.

select 
 test.id as 'id'
from 
 test
where 
 test.id = 2
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.