SQL DISTINCT och TOP i samma fråga
Den här artikeln är inspirerad av en serie frågor som en av mina läsare, Nan, nyligen skickade till mig om DISTINCT, TOP och ORDER BY.
Alla exemplen i den här lektionen är baserade på Microsoft SQL Server Management Studio och databasen AdventureWorks2012. Du kan börja använda dessa kostnadsfria verktyg med hjälp av min guide Getting Started Using SQL Server.
Hur fungerar SQL Top och Distinct SELECT-modifierna tillsammans för att producera resultat?
Här är frågan som Nan ursprungligen skickade till mig:
Jag är lite förvirrad om SELECT DISTINCT och SELECT. Till exempel,
SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName
Söker detta efter olika förnamn? Skilda kombinerade för- och efternamn? Hur skiljer vi mellan de kolumner som används för den distinkta utvärderingen och de kolumner som vi bara vill visa i resultatet?
Hur är det med
Select Distinct TOP 10 LastName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY LastName
Jag trodde att alla ville veta svaret så jag skapar ett blogginlägg.
DISTINCT och TOP – vilket är först?
Låt oss titta på det första uttalandet vars syfte är att returnera en unik lista med för- och efternamn.
SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName;
TOP 10 kommer att returnera de tio första posterna från den ordnade mängden, och DISTINCT kommer att ta bort eventuella dubbletter. Frågan är vad som händer först?
- Är tabellen sorterad efter efternamn och de tio bästa objekten tas fram, och sedan tas dubbletter bort?
- Och tas dubbletter bort, och sedan sorteras objekten och de tio bästa objekten visas?
För att vi ska besvara den här frågan ska vi komma ihåg att DISTINCT verkar på alla kolumner och uttryck i SELECT-klausulen. Så i det här fallet kommer uttalandet att returnera skilda rader för FirstName och LastName.
Det finns tyvärr inget direkt sätt att använda DISTINCT på en uppsättning fält och visa andra. När du lägger till kolumner i SELECT-anvisningen blir de påverkade av DISTINCT-operatören. Jag säger direkt, eftersom du kan få en distinkt lista och sedan använda en INNER JOIN för att dra in andra kolumner. Det finns dock faror med att göra det, eftersom joinen kan återinföra dubbletter.
Att lägga till en TOP-klausul till DISTINCT är intressant. Jag var inte säker på vad som skulle hända, men jag experimenterade lite med AdventureWorks-databasen och upptäckte att bearbetningsordningen går ungefär så här:
- Välj DISTINCT-värden från tabellen och beställ
- Välj TOP x rader från resultaten i steg 1 och visa dem.
Om du vill pröva det här själv, börja med
SELECT FirstName,
LastName
FROM Person.Person
ORDER BY LastName
Och notera resultaten. Håll koll på ”Kim Ambercombie”. Lägg märke till att det finns tre poster för hennes namn.
Kör nu
SELECT DISTINCT FirstName,
LastName
FROM Person.Person
ORDER BY LastName
Och du kommer att se att ”Kim Ambercombine” bara visas en gång.
Nu körs
SELECT DISTINCT TOP 10 FirstName,
LastName
FROM Person.Person
ORDER BY LastName
Och du ser att den returnerar de tio första unika för- och efternamnen sorterade efter LastName.
Om du undrar vad som händer först, DISTINCT- eller TOP 10-operationerna, jämför då resultaten från de två senaste frågorna.
Bemärk att frågan ”DISTINCT TOP 10” inkluderar de första 10 raderna från frågan från ”DISTINCT”-frågan.
Från detta vet vi att en DISTINCT-lista först skapas och sedan returneras TOP 10-elementen.
Du kan också bekräfta detta genom att visa sökningsplanen. För att göra det väljer du Query -> Include Actual Query Plan i menyn innan du utför frågan.
Ikonen ”Stream Aggregate” står för DISTINCT-operationen och ”Top” för TOP 10-operationen.
Det kan tyckas vara något kontraintuitivt att se DISTINCT listat först i SELECT-angivelsen. Tänk på att SQL inte nödvändigtvis behandlas i den ordning som en människa skulle läsa det från vänster till höger.
DISTINCT och TOP med SELECT-listuttryck
Den andra delen av Nans fråga gällde hur uttryck behandlas med DISTINCT-operatorn.
Uttryck behandlas på samma sätt som kolumner när det gäller DISTINCT och TOP. Låt oss börja med ett select-statement för att få fram förnamnet samt det fullständiga, som vi skapar genom att lägga till LastName till FirstName.
Tänk också på, när du använder ORDER BY, att ORDER BY-elementen måste finnas med i select-listan när du använder Distinct. Med tanke på detta måste jag ändra det uttalande som presenterades i den ursprungliga frågan:
SELECT DISTINCT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY LastName
Kan inte köras eftersom LastName inte finns med i SELECT-listan. Ja, det är en del av ett uttryck i select-listan, men det finns inte där på egen hand. Det är giltigt att ordna efter FullName.
Vi kommer att använda denna ordning i exemplen nedan.
Statementet
SELECT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName
Returnerar 19972 rader. När vi lägger till Distinct
SELECT DISTINCT FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName
Då returneras 19516 rader. När vi slutligen lägger till Top 10 returneras de 10 första distinkta namnkombinationerna.
SELECT DISTINCT TOP 10 FirstName,
FirstName + ' ' + LastName AS FullName
FROM Person.Person
ORDER BY FirstName + ' ' + LastName
Varsågod och kör dessa frågor på AdventureWork-databasen så kan du själv se att beteendet är detsamma som när vi enbart arbetar med kolumner.