Esaminare l'istruzione SELECT

Completato

Transact-SQL o T-SQL è un dialetto del linguaggio SQL standard ANSI usato dai prodotti e dai servizi Microsoft SQL. È simile a SQL standard. Qui verrà esaminata principalmente l'istruzione SELECT, che include di gran lunga il maggior numero di opzioni e varianti di qualsiasi istruzione DML.

Per iniziare, osservare attentamente il modo in cui viene elaborata un'istruzione SELECT. L'ordine in cui un'istruzione SELECT viene scritta non corrisponde all'ordine in cui viene valutata ed elaborata dal motore di database di SQL Server.

Si consideri la query seguente:

SELECT OrderDate, COUNT(OrderID) AS Orders
FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate
HAVING COUNT(OrderID) > 1
ORDER BY OrderDate DESC;

La query è costituita da un'istruzione SELECT, composta da più clausole, ognuna delle quali definisce un'operazione specifica che deve essere applicata ai dati recuperati. Prima di esaminare l'ordine di esecuzione delle operazioni, si osserverà brevemente il funzionamento della query, anche se i dettagli delle varie clausole non verranno trattati in questo modulo.

La clausola SELECT restituisce la colonna OrderDate e il conteggio dei valori OrderID, a cui viene assegnato il nome (o alias) Orders:

SELECT OrderDate, COUNT(OrderID) AS Orders

La clausola FROM identifica la tabella da cui hanno origine le righe per la query; in questo caso è la tabella Sales.SalesOrder:

FROM Sales.SalesOrder

La clausola WHERE filtra le righe dai risultati, mantenendo solo quelle che soddisfano la condizione specificata; in questo caso gli ordini con stato "spedito":

WHERE Status = 'Shipped'

La clausola GROUP BY accetta le righe che hanno soddisfatto la condizione del filtro e le raggruppa in base a OrderDate, in modo che tutte le righe con lo stesso OrderDate siano considerate come un singolo gruppo e che venga restituita una riga per ogni gruppo:

GROUP BY OrderDate

Dopo che sono stati formati, i gruppi vengono filtrati dalla clausola HAVING in base al suo predicato. Nei risultati verranno incluse solo le date con più di un ordine:

HAVING COUNT(OrderID) > 1

Per l'anteprima di questa query, la clausola finale è ORDER BY, che mette l'output in ordine decrescente di OrderDate:

ORDER BY OrderDate DESC;

Ora che sono state esaminate le funzioni di ogni clausola, è possibile esaminare l'ordine effettivo in cui vengono valutate da SQL Server:

  1. La prima a essere valutata è la clausola FROM, per fornire le righe di origine per il resto dell'istruzione. Viene creata una tabella virtuale, poi inviata al passaggio successivo.
  2. La successiva a essere valutata è la clausola WHERE, che filtra le righe della tabella di origine che corrispondono a un predicato. La tabella virtuale filtrata viene inviata al passaggio successivo.
  3. La successiva è GROUP BY, che organizza le righe nella tabella virtuale in base ai valori univoci trovati nell'elenco GROUP BY. Viene creata una nuova tabella virtuale contenente l'elenco dei gruppi, che poi viene inviata al passaggio successivo. Da questo punto del flusso di operazioni gli elementi possono fare riferimento solo alle colonne nell'elenco GROUP BY o alle funzioni di aggregazione.
  4. La successiva a essere valutata è la clausola HAVING, che filtra interi gruppi in base al predicato. La tabella virtuale creata al passaggio 3 viene filtrata e inviata al passaggio successivo.
  5. Infine viene eseguita la clausola SELECT, che determina le colonne che verranno visualizzate nei risultati della query. Poiché la clausola SELECT viene valutata dopo gli altri passaggi, gli alias delle colonne creati al suo interno (in questo esempio Orders) non possono essere usati nella clausola GROUP BY o HAVING.
  6. La clausola ORDER BY è l'ultima a essere eseguita e ordina le righe in base al suo elenco di colonne.

Per applicare queste conoscenze alla query di esempio, di seguito è definito l'ordine logico di esecuzione dell'istruzione SELECT riportata sopra:

FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate 
HAVING COUNT(OrderID) > 1
SELECT OrderDate, COUNT(OrderID) AS Orders
ORDER BY OrderDate DESC;

Non tutte le possibili clausole sono obbligatorie in ogni istruzione SELECT che viene scritta. L'unica clausola obbligatoria è SELECT, che in alcuni casi può essere usata da sola. In genere viene inclusa anche una clausola FROM per identificare la tabella in cui viene eseguita la query. Transact-SQL include anche altre clausole che possono essere aggiunte.

Quindi, le query T-SQL non vengono scritte nello stesso ordine in cui vengono valutate logicamente. L'ordine di esecuzione della valutazione determina i dati disponibili per ogni clausola, perché una clausola può accedere solo alle informazioni rese disponibili da una clausola già elaborata. Per questo motivo, quando si scrivono le query è importante capire il vero ordine logico di elaborazione.

Selezione di tutte le colonne

La clausola SELECT viene spesso definita come elenco SELECT, perché elenca i valori da restituire nei risultati della query.

La forma più semplice di una clausola SELECT è l'uso del carattere asterisco (*) per restituire tutte le colonne. Quando viene usato nelle query T-SQL, viene chiamato stella. Sebbene la clausola SELECT * sia adatta per un test rapido, è necessario evitare di usarla nei processi di produzione per i motivi seguenti:

  • Le modifiche alla tabella che comportano l'aggiunta o la riorganizzazione delle colonne verranno rispecchiate nei risultati della query e questo può comportare un output imprevisto per le applicazioni e i report che usano la query.
  • La restituzione di dati non necessari può rallentare le query e causare problemi di prestazioni se la tabella di origine contiene un numero elevato di righe.

Nell'esempio seguente vengono recuperate tutte le colonne dalla tabella (ipotetica) Production.Product.

SELECT * FROM Production.Product;

Il risultato di questa query è un set di righe che contiene tutte le colonne per tutte le righe della tabella. L'aspetto potrebbe essere simile al seguente:

ProductID

Name

ProductNum

Color

StandardCost

ListPrice

Dimensione

Peso

ProductCatID

680

HL Road Frame - Nero, 58

FR-R92B-58

Nero

1.059,31

1.431,5

58

1016,04

18

706

HL Road Frame - Rosso, 58

FR-R92R-58

Rosso

1.059,31

1.431,5

58

1016,04

18

707

Sport-100 Casco, Rosso

HL-U509-R

Rosso

13,0863

34,99

35

708

Sport-100 Casco, Nero

HL U509

Nero

13,0863

34,99

35

...

...

...

...

...

...

...

...

...

Selezione di colonne specifiche

Un elenco esplicito di colonne consente di controllare esattamente le colonne che vengono restituite e il loro ordine. L'intestazione di ogni colonna nel risultato corrisponderà al nome della colonna.

Ad esempio, prendere in considerazione la query seguente, che usa ancora una volta la tabella ipotetica Production.Product.

SELECT ProductID, Name, ListPrice, StandardCost
‎FROM Production.Product;

Questa volta, i risultati includono solo le colonne specificate:

ProductID

Name

ListPrice

StandardCost

680

HL Road Frame - Nero, 58

1.431,5

1.059,31

706

HL Road Frame - Rosso, 58

1.431,5

1.059,31

707

Sport-100 Casco, Rosso

34,99

13,0863

708

Sport-100 Casco, Nero

34,99

13,0863

...

...

...

...

Selezione delle espressioni

Oltre a recuperare le colonne archiviate nella tabella specificata, una clausola SELECT può eseguire calcoli e modifiche, che usano operatori per combinare colonne e valori o più colonne. Il risultato del calcolo o della modifica deve essere un risultato a valore singolo (scalare) che verrà visualizzato nel risultato come colonna separata.

Ad esempio, la query seguente include due espressioni:

SELECT ProductID,
      Name + '(' + ProductNumber + ')',
  ListPrice - StandardCost
FROM Production.Product;

I risultati di questa query potrebbero essere simili ai seguenti:

ProductID

680

HL Road Frame - Black, 58(FR-R92B-58)

372,19

706

HL Road Frame - Red, 58(FR-R92R-58)

372,19

707

Sport-100 Helmet, Red(HL-U509-R)

21,9037

708

Sport-100 Helmet, Black(HL-U509)

21,9037

...

...

...

Questi risultati presentano un paio di aspetti interessanti da tenere presenti:

  • Le colonne restituite dalle due espressioni non hanno nomi. A seconda dello strumento usato per inviare la query, un nome di colonna mancante potrebbe essere indicato da un'intestazione di colonna vuota, da un valore letterale "nessun nome di colonna" o da un nome predefinito come column1. Più avanti in questa sezione verrà illustrato come specificare un alias per il nome della colonna nella query.
  • La prima espressione usa l'operatore + per concatenare i valori stringa (basati su caratteri), mentre la seconda espressione usa l'operatore - per sottrarre un valore numerico da un altro. Quando viene usata con valori numerici, l'operatore + esegue un'addizione. È quindi evidente che è importante capire i tipi di dati delle colonne incluse nelle espressioni. I tipi di dati verranno illustrati nella sezione successiva.

Specifica degli alias delle colonne

È possibile specificare un alias per ogni colonna restituita dalla query SELECT, come alternativa al nome della colonna di origine o per assegnare un nome all'output di un'espressione.

Ad esempio, qui è riportata la stessa query di prima ma con alias specificati per ogni colonna:

SELECT ProductID AS ID,
      Name + '(' + ProductNumber + ')' AS ProductName,
  ListPrice - StandardCost AS Markup
FROM Production.Product;

I risultati di questa query includono i nomi di colonna specificati:

ID

ProductName

Markup

680

HL Road Frame - Black, 58(FR-R92B-58)

372,19

706

HL Road Frame - Red, 58(FR-R92R-58)

372,19

707

Sport-100 Helmet, Red(HL-U509-R)

21,9037

708

Sport-100 Helmet, Black(HL-U509)

21,9037

...

...

...

Nota

Quando si specifica un alias, la parola chiave AS è facoltativa ma è consigliabile includerla per chiarezza.

Formattazione delle query

Dagli esempi in questa sezione è possibile notare che la scelta del modo di formattare il codice della query può essere effettuata con una certa flessibilità. Ad esempio è possibile scrivere ogni clausola (o l'intera query) in una singola riga oppure suddividerla in più righe. Nella maggior parte dei sistemi di database il codice non fa distinzione tra maiuscole e minuscole e alcuni elementi del linguaggio T-SQL sono facoltativi (inclusi la parola chiave AS, come indicato in precedenza, e il punto e virgola alla fine di un'istruzione).

Prendere in considerazione le linee guida seguenti per rendere facilmente leggibile il codice T-SQL, in modo da semplificarne la comprensione e il debug:

  • Scrivere in lettere maiuscole le parole chiave T-SQL, come SELECT, FROM, AS e così via. L'uso delle lettere maiuscole per le parole chiave è una convenzione usata comunemente che facilita l'individuazione di ogni clausola in un'istruzione complessa.
  • Iniziare una nuova riga per ogni clausola principale di un'istruzione.
  • Se l'elenco SELECT contiene più di qualche alias, espressione o colonna, è consigliabile elencare ogni colonna in una riga a sé stante.
  • Impostare un rientro per le righe contenenti sottoclausole o colonne, in modo da specificare quale codice appartiene a ogni clausola principale.