SQL DISTINCT e TOP nella stessa query
Questo articolo è ispirato da una serie di domande che uno dei miei lettori, Nan, mi ha recentemente inviato riguardo DISTINCT, TOP, e ORDER BY.
Tutti gli esempi di questa lezione sono basati su Microsoft SQL Server Management Studio e il database AdventureWorks2012. Puoi iniziare a usare questi strumenti gratuiti usando la mia guida Getting Started Using SQL Server.
Come funzionano insieme i modificatori SQL Top e Distinct SELECT per produrre risultati?
Ecco la domanda che Nan mi ha originariamente inviato:
Sono un po’ confuso su SELECT DISTINCT e SELECT. Per esempio,
SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName
Si tratta di nomi distinti? Nomi e cognomi combinati distinti? Come facciamo a distinguere tra le colonne usate per la valutazione distinta e le colonne che vogliamo solo mostrare nell’output?
Che dire di
Select Distinct TOP 10 LastName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY LastName
Ho pensato che tutti vorrebbero sapere la risposta così ho creato un post sul blog.
DISTINCT e TOP – Qual è il primo?
Guardiamo la prima istruzione il cui scopo è di restituire una lista unica di pugni e cognomi.
SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName;
TOP 10 restituirà i primi dieci elementi dell’insieme ordinato, e DISTINCT rimuoverà i duplicati. La domanda è: cosa succede prima?
- La tabella viene ordinata per LastName e vengono presi i primi dieci elementi, e poi vengono rimossi i duplicati?
- O vengono rimossi i duplicati, e poi gli elementi vengono ordinati e vengono visualizzati i primi dieci elementi?
Prima di rispondere a questa domanda tenete a mente che DISTINCT opera su tutte le colonne ed espressioni nella clausola SELECT. Quindi, in questo caso, l’istruzione restituirà righe distinte per FirstName e LastName.
Purtroppo non c’è un modo diretto per usare DISTINCT su un set di campi e visualizzare gli altri. Una volta aggiunte le colonne all’istruzione SELECT, esse diventano sotto l’influenza dell’operatore DISTINCT. Dico diretto, perché si potrebbe ottenere una lista distinta, e poi usare una INNER JOIN per tirare dentro altre colonne. Ci sono però dei pericoli nel farlo, poiché il join potrebbe reintrodurre i duplicati.
Aggiungere una clausola TOP a DISTINCT è interessante. Non ero sicuro di cosa sarebbe successo, ma ho fatto qualche esperimento con il database di AdventureWorks e ho scoperto che l’ordine di elaborazione va in questo modo:
- Seleziona i valori DISTINCT dalla tabella e ordina
- Seleziona le righe TOP x dai risultati del passo 1 e visualizza.
Se vuoi provarlo tu stesso inizia con
SELECT FirstName,
LastName
FROM Person.Person
ORDER BY LastName
E nota i risultati. Tenete traccia di “Kim Ambercombie”. Nota come ci sono tre voci per il suo nome.
Ora esegui
SELECT DISTINCT FirstName,
LastName
FROM Person.Person
ORDER BY LastName
E vedrai che “Kim Ambercombine” è mostrato solo una volta.
Esegui
SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName
E vedrai che restituisce i primi 10 nomi e cognomi unici ordinati per Cognome.
Se vi state chiedendo cosa succede prima, le operazioni DISTINCT o TOP 10, allora confrontate i risultati delle ultime due query.
Nota che la query “DISTINCT TOP 10” include le prime 10 righe della query della query “DISTINCT”.
Da questo sappiamo che prima viene creata una lista DISTINCT, e poi vengono restituiti i TOP 10 elementi.
Puoi anche confermare questo mostrando il piano della query. Per farlo, seleziona Query -> Includi piano di query effettivo dal menu prima di eseguire la query.
L’icona “Stream Aggregate” è per l’operazione DISTINCT e “Top” per quella TOP 10.
Può sembrare un po’ controintuitivo vedere DISTINCT elencato per primo nella dichiarazione SELECT. Basta tenere a mente che l’SQL non viene necessariamente elaborato nell’ordine in cui un umano lo leggerebbe da sinistra a destra.
DISTINCT e TOP con le espressioni della lista SELECT
La seconda parte della domanda di Nan riguardava il trattamento delle espressioni con l’operatore DISTINCT.
Le espressioni sono trattate come le colonne per quanto riguarda DISTINCT e TOP. Cominciamo con una dichiarazione di selezione per ottenere il primo nome e il nome completo, che creiamo aggiungendo LastName a FirstName.
Inoltre, tenete a mente, quando usate ORDER BY, che gli elementi ORDER BY devono apparire nella lista di selezione quando usate Distinct. Dato questo devo modificare la dichiarazione presentata nella domanda originale:
SELECT DISTINCT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY LastName
Non verrà eseguita poiché LastName non è nella lista SELECT. Sì, è parte di un’espressione nella lista di selezione, ma non è lì da sola. E’ valido ordinare per FullName.
Utilizzeremo questo ordinamento negli esempi seguenti.
Lo statement
SELECT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName
Ritorna 19972 righe. Quando aggiungiamo Distinct
SELECT DISTINCT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName
Allora vengono restituite 19516 righe. Infine aggiungendo Top 10, vengono restituite le prime 10 combinazioni di nomi distinti.
SELECT DISTINCT TOP 10 FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName
Prova ad eseguire queste query sul database di AdventureWork e vedrai tu stesso che il comportamento è lo stesso che troviamo quando lavoriamo esclusivamente con le colonne.