Perché le query vengono analizzate in modo tale da non consentire l'uso degli alias di colonna nella maggior parte delle clausole?


16

Durante il tentativo di scrivere una query, ho scoperto (nel modo più duro) che SQL Server analizza DOVE in una query molto prima di analizzare SELECTs durante l'esecuzione di una query.

I documenti MSDN affermano che l'ordine generale di analisi logica è tale che SELECT viene analizzato quasi per ultimo (risultando così in errori "nessun tale oggetto [alias]" quando si tenta di utilizzare un alias di colonna in altre clausole). C'è stato anche un suggerimento per consentire l'utilizzo di alias ovunque, che è stato abbattuto dal team di Microsoft, citando problemi di conformità agli standard ANSI (che suggerisce che questo comportamento è parte dello standard ANSI).

Come programmatore (non un DBA), ho trovato questo comportamento un po 'confuso, dal momento che mi sembra che sconfigge in gran parte lo scopo di avere alias di colonna (o, quantomeno, alias di colonna potrebbero essere resi significativamente più potenti se fossero analizzato in precedenza nell'esecuzione della query), poiché l'unico posto in cui è possibile utilizzare effettivamente gli alias è ORDER BY. Come programmatore, sembra che manchi un'enorme opportunità per rendere le query più potenti, convenienti e ASCIUTTE.

Sembra che sia un problema così evidente che è logico, quindi, che ci siano altri motivi per decidere che gli alias di colonna non dovrebbero essere consentiti in qualcosa di diverso da SELECT e ORDER BY, ma quali sono questi motivi?

Risposte:


19

Sommario

Non c'è alcun motivo logico per cui non si possa fare, ma il vantaggio è piccolo e ci sono alcune insidie ​​che potrebbero non essere immediatamente evidenti.

Risultati della ricerca

Ho fatto delle ricerche e ho trovato delle buone informazioni. Quanto segue è una citazione diretta da una fonte primaria affidabile (che desidera rimanere anonima) alle 09-08-2012 17:49 GMT:

Quando SQL è stato inventato per la prima volta, non aveva alias nella clausola SELECT. Questo fu un grave difetto che fu corretto quando la lingua fu standardizzata dall'ANSI verso il 1986.

La lingua doveva essere "non procedurale", in altre parole, per descrivere i dati desiderati senza specificare come trovarli. Quindi, per quanto ne so, non c'è motivo per cui un'implementazione SQL non possa analizzare l'intera query prima di elaborarla e consentire agli alias di essere definiti ovunque e utilizzati ovunque. Ad esempio, non vedo alcun motivo per cui la seguente query non dovrebbe essere valida:

select name, salary + bonus as pay
from employee
where pay > 100000

Sebbene ritenga che questa sia una domanda ragionevole, alcuni sistemi basati su SQL possono introdurre restrizioni sull'uso degli alias per qualche motivo relativo all'implementazione. Non sono sorpreso di sapere che SQL Server fa questo.

Sono interessato a ulteriori ricerche sullo standard SQL-86 e sul perché i moderni DBMS non supportano il riutilizzo dell'alias, ma non ho ancora avuto il tempo di andare molto lontano. Per i principianti, non so dove ottenere la documentazione o come scoprire chi ha creato esattamente il comitato. Qualcuno può dare una mano? Vorrei anche sapere di più sul prodotto Sybase originale da cui proveniva SQL Server.

Da questa ricerca e da qualche altro pensiero, sono arrivato a sospettare che l'uso di alias in altre clausole, sebbene del tutto possibile, semplicemente non sia mai stata una priorità così alta per i produttori di DBMS rispetto ad altre funzionalità linguistiche. Dal momento che non è un grosso ostacolo, essendo facilmente aggirabile dallo scrittore di query, lo sforzo su altri progressi non è ottimale. Inoltre, sarebbe proprietario in quanto ovviamente non fa parte dello standard SQL (anche se sto aspettando di saperne di più su questo di sicuro) e quindi sarebbe un piccolo miglioramento, rompendo la compatibilità SQL tra DBMS. In confronto, CROSS APPLY(che in realtà non è altro che una tabella derivata che consente riferimenti esterni) è un enorme cambiamento, che mentre il proprietario offre un'incredibile potenza espressiva non facilmente eseguibile in altri modi.

Problemi con l'utilizzo degli alias ovunque

Se permetti che gli elementi SELECT vengano inseriti nella clausola WHERE, non solo puoi esplodere la complessità della query (e quindi la complessità di trovare un buon piano di esecuzione), ma è possibile inventare cose completamente illogiche. Provare:

SELECT X + 5 Y FROM MyTable WHERE Y = X

Cosa succede se MyTable ha già una colonna Y, a quale si riferisce la clausola WHERE? La soluzione consiste nell'utilizzare un CTE o una tabella derivata, che nella maggior parte dei casi non dovrebbe costare alcun extra ma raggiunge lo stesso risultato finale finale. I CTE e le tabelle derivate applicano almeno la risoluzione dell'ambiguità consentendo di utilizzare un alias una sola volta.

Inoltre, non usare alias nella clausola FROM ha un senso eminente. Non puoi farlo:

SELECT
   T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
FROM
   Table1 T
   INNER JOIN Table2 T2
      ON T2.ID = CalcID
   INNER JOIN Table3 T3
      ON T2.ID = T3.ID

È un riferimento circolare (nel senso che T2 fa segretamente riferimento a un valore di T3, prima che quella tabella sia stata presentata nell'elenco JOIN), e dannatamente difficile da vedere. Che ne dici di questo:

INSERT dbo.FinalTransaction
SELECT
   newid() FinalTransactionGUID,
   'GUID is: ' + Convert(varchar(50), FinalTransactionGUID) TextGUID,
   T.*
FROM
   dbo.MyTable T

Quanto vuoi scommettere che la funzione newid () verrà inserita nel piano di esecuzione due volte, rendendo inaspettatamente completamente le due colonne che mostrano valori diversi? Che dire di quando viene utilizzata la query sopra N livelli profondi in CTE o tabelle derivate. Garantisco che il problema è peggiore di quanto tu possa immaginare. Esistono già seri problemi di incoerenza su quando le cose vengono valutate una sola volta o in quale punto del piano di query e Microsoft ha affermato che non risolveràalcuni perché esprimono correttamente l'algebra delle query - se si ottengono risultati imprevisti, suddividere la query in parti. Consentire riferimenti concatenati, rilevare riferimenti circolari attraverso catene potenzialmente molto lunghe, sono problemi piuttosto delicati. Introduci il parallelismo e hai un incubo in preparazione.

Nota: l'uso dell'alias in WHERE o GROUP BY non farà la differenza per i problemi con funzioni come newid () o rand ().

Un modo di SQL Server per creare espressioni riutilizzabili

CROSS APPLY / OUTER APPLY è un modo in SQL Server per creare espressioni che possono essere utilizzate in qualsiasi altro punto della query (appena non prima nella clausola FROM):

SELECT
   X.CalcID
FROM
   Table1 T
   INNER JOIN Table3 T3
      ON T.ID = T3.ID
   CROSS APPLY (
      SELECT
         T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
   ) X
   INNER JOIN Table2 T2
      ON T2.ID = X.CalcID

Questo fa due cose:

  1. Rende tutte le espressioni in CROSS APPLY uno "spazio dei nomi" (un alias di tabella, qui, X) e diventa unica all'interno di quello spazio dei nomi.
  2. Rende evidente dappertutto non solo che CalcID proviene da X, ma rende anche ovvio il motivo per cui non è possibile utilizzare nulla da X quando si uniscono le tabelle T1 e T3, poiché X non è stato ancora introdotto.

In realtà sono abbastanza affezionato a CROSS APPLY. È diventato il mio fedele amico, e lo uso sempre. Hai bisogno di un UNPIVOT parziale (che richiederebbe un PIVOT / UNPIVOT o UNPIVOT / PIVOT usando la sintassi nativa)? Fatto con CROSS APPLY. Hai bisogno di un valore calcolato che verrà riutilizzato più volte? Fatto. È necessario applicare rigidamente l'ordine di esecuzione per le chiamate su un server collegato? Fatto-con un urlante miglioramento della velocità. Hai bisogno di un solo tipo di riga divisa in 2 righe o con condizioni extra? Fatto.

Quindi, almeno, in DBMS SQL Server 2005 e versioni successive, non hai ulteriori motivi di reclamo: CROSS APPLY è il modo in cui ASCIUGA nel modo che desideri.


14

Non posso dirti i motivi esatti, ma ti dirò che ci sono soluzioni alternative alle espressioni ripetute, ad esempio l'uso di CTE, sottoquery, tabelle derivate ecc. Per evitare la ripetizione.

Se mostri una query con un'espressione ripetuta, possiamo probabilmente mostrarti come riscriverla in modo che l'espressione sia elencata una sola volta. Tuttavia, ciò riduce solo la complessità nella scrittura / lettura della query, è improbabile che cambi molto sull'efficienza. SQL Server è generalmente abbastanza bravo nel riconoscere che le espressioni si ripetono e non eseguirà quel lavoro due volte. Ci sono eccezioni che vanno nella direzione opposta, ma dovresti preoccuparti dell'efficienza solo quando osservi realmente questo accadere. Ho il sospetto che la maggior parte delle espressioni ripetute che scrivi siano realmente compresse in un'unica operazione nel piano.

Detto questo, ripeterò anche parte della mia risposta da questa domanda:

/dba/19762/why-is-the-select-clause-listed-first


Ecco la spiegazione di Joe Celko su come una query viene elaborata secondo lo standard (ho rubato questo dal mio articolo aspfaq.com , che ha rubato la citazione probabilmente da un post di newsgroup di Celko):

Ecco come funziona un SELECT in SQL ... almeno in teoria. I prodotti reali ottimizzeranno le cose quando possono.

Inizia nella clausola FROM e costruisci una tabella di lavoro da tutti i join, i sindacati, le intersezioni e qualsiasi altro costruttore di tabelle ci sia. L'opzione AS consente di assegnare un nome a questa tabella di lavoro che è necessario utilizzare per il resto della query contenente.

Vai alla clausola WHERE e rimuovi le righe che non superano i criteri; cioè, che non testano su VERO (respingono SCONOSCIUTI e FALSI). La clausola WHERE viene applicata al funzionamento nella clausola FROM.

Vai alla clausola GROUP BY opzionale, crea gruppi e riduci ogni gruppo a una singola riga, sostituendo la tabella di lavoro originale con la nuova tabella raggruppata. Le righe di una tabella raggruppata devono essere caratteristiche di gruppo: (1) una colonna di raggruppamento (2) una statistica sul gruppo (cioè funzioni aggregate) (3) una funzione o (4) un'espressione composta da questi tre elementi.

Vai alla clausola HAVING opzionale e applicala al tavolo di lavoro raggruppato; se non vi era alcuna clausola GROUP BY, trattare l'intera tabella come un gruppo.

Vai alla clausola SELECT e costruisci le espressioni nell'elenco. Ciò significa che le sottoquery scalari, le chiamate di funzione e le espressioni in SELECT vengono eseguite dopo che sono state eseguite tutte le altre clausole. L'operatore AS può anche dare un nome alle espressioni nell'elenco SELECT. Questi nuovi nomi nascono tutti in una volta, ma dopo che la clausola WHERE è stata eseguita; non è possibile utilizzarli nell'elenco SELECT o nella directory WHERE per tale motivo.

Le espressioni di query nidificate seguono le normali regole di ambito che ci si aspetterebbe da un linguaggio strutturato a blocchi come C, Pascal, Algol, ecc. Vale a dire, le query più interne possono fare riferimento a colonne e tabelle nelle query in cui sono contenute.

Ciò significa che un SELECT non può avere più colonne di un GROUP BY; ma sicuramente può avere meno colonne.

Ora, Celko è stato uno dei principali contributori alle precedenti versioni degli standard. Non so se avrai mai una risposta definitiva alla WHY?domanda, tranne che per le speculazioni. La mia ipotesi è che elencando prima l'operazione effettiva sia molto facile per il parser sapere esattamente quale sarà il tipo di operazione. Immagina un join di 20 tavoli che potrebbe finire per essere un SELECTo UPDATEoDELETE , e ricordare che il codice per questi motori è stato originariamente scritto indietro ai tempi in cui stringa parsing era abbastanza costoso.

Si noti che se lo standard SQL è dettato FROM di venire per primo, i fornitori potrebbero aver deciso autonomamente di analizzare la grammatica in un ordine diverso, quindi potrebbe non avere senso aspettarsi che l'ordine delle clausole come scritto obbedisca completamente all'ordine di elaborazione del 100% di il tempo.

Lo stesso vale per cose come CASE. Abbiamo visto scenari proprio qui su questo sito , ad esempio, in cui il mito precedentemente creduto che CASEelabora sempre in ordine e in corto circuito, è falso. E questo si estende anche ad altre credenze comuni, come SQL Server che valuta i join nell'ordine in cui sono stati scritti, le clausole di cortocircuito WHEREda sinistra a destra o l'elaborazione di CTE una volta o in un certo ordine, anche se vengono referenziati più volte. I prodotti sono liberi di ottimizzare il modo in cui ritengono opportuno anche se non riflette esattamente il modo in cui hai dichiarato che la query dovrebbe funzionare in modo dichiarativo.


2
Si noti inoltre che la capacità di utilizzare o meno gli alias in diverse parti della query è imposta dal parser, non dall'ottimizzatore o dal motore di esecuzione. Il modo in cui il motore esegue effettivamente la query non riflette necessariamente le restrizioni che incidono sulla sintassi.
Aaron Bertrand

2

In Entity SQL , è possibile utilizzare gli alias delle espressioni in altri punti della query in alcune situazioni:

select k1, count(t.a), sum(t.a)
from T as t
group by t.b + t.c as k1

Nota che qui DEVI definire l'espressione nella GROUP BYclausola per usarla nella SELECTclausola.

È ovviamente possibile consentire questo tipo di alias-come-riutilizzabile-espressione nelle query SQL.

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.