När en rapport visar samma land eller e-postadress flera gånger blir resultatet snabbt svårläst. Med DISTINCT får du vanligtvis en rad per unikt värde, men lägger du till fler kolumner ändras definitionen av vad som räknas som unikt.
Ta bort dubletter i resultatet utan att ändra tabellen
- En kolumn: SQL visar varje unikt värde en gång.
- Flera kolumner: unika kombinationer visas, inte unika värden i den första kolumnen.
- Räkna värden: använd
COUNT(DISTINCT kolumn). - Tomma värden: flera
NULL-värden ger normalt en rad i resultatet medDISTINCT. - Dubletter i tabellen: frågan ändrar inte eller raderar lagrade rader.
En kolumn ger en lista med unika värden
Behöver du exempelvis lista alla länder som förekommer i en kundtabell räcker en kolumn i resultatet. Skriv DISTINCT direkt efter SELECT:
SELECT DISTINCT land
FROM kunder;Om tabellen innehåller många kunder i Sverige men bara en i Norge visas ändå varje land en gång. Resultatet blir en kortare lista, medan raderna i tabellen förblir oförändrade.
Det är en skillnad som lätt försvinner när man snabbt testar en fråga. DISTINCT rensar bara dubbletter från resultatuppsättningen, inte från databasen.
Flera kolumner gör kombinationen unik
Två kolumner kan se ut att innehålla dubbletter, fast hela raden skiljer sig. Anta att en webbplats har registrerat följande kombinationer av land och språk:
| land | språk |
|---|---|
| Sverige | svenska |
| Sverige | engelska |
| Sverige | svenska |
| Norge | norska |
Frågan SELECT DISTINCT land, språk FROM besök; returnerar tre unika kombinationer. Sverige syns två gånger eftersom landet hör ihop med två olika språk. SQL jämför hela kolumnkombinationen, inte bara den första kolumnen.
Om du behöver en rad per land men också vill visa ett språk måste du bestämma vilket språk som ska representera landet, eller använda en aggregering. Att lägga till en kolumn i SELECT är alltså inte ett oskyldigt sätt att få mer information; det kan göra att fler rader blir unika.
Välj mellan DISTINCT, GROUP BY och COUNT
Tre frågor kan låta lika men kräva olika SQL. Jag tycker att den tydligaste tumregeln är att välja syntax efter vad resultatet ska säga.
- Vill du visa en lista med unika värden? Använd
SELECT DISTINCT kolumn. - Vill du räkna olika värden? Använd
COUNT(DISTINCT kolumn). - Vill du få en rad per kategori och beräkna antal eller summa för varje kategori? Använd
GROUP BY.
Exempelvis ger SELECT COUNT(DISTINCT land) FROM kunder; antalet olika länder. Den returnerar ett enda tal, inte en lista över länder. Med GROUP BY land kan frågan i stället visa varje land tillsammans med exempelvis antalet kunder.
Här är beslutet i praktiken: lista värden med DISTINCT, räkna unika värden med COUNT(DISTINCT), gruppera när varje grupp ska få egna beräkningar. Då slipper du använda DISTINCT som en generell lösning på resultat som ser oväntade ut.
NULL och JOIN kan förklara oväntade resultat
En rad med NULL betyder att värdet saknas eller är okänt, inte att det är en tom text. I MySQL räknas flera NULL-värden som lika vid DISTINCT, så en fråga på kolumnen kan visa ett enda NULL. För att filtrera bort saknade värden behöver du lägga till WHERE kolumn IS NOT NULL.
Om samma kund dyker upp flera gånger efter en koppling mellan tabeller kan en JOIN vara orsaken. En kund med flera ordrar får exempelvis en resultatrad per order. Tabellen nedan hjälper dig skilja mellan de vanligaste situationerna:
| Situation | Trolig orsak | Gör så här |
|---|---|---|
| En kolumn har samma värde flera gånger | Flera poster delar värdet | Använd SELECT DISTINCT kolumn för en unik lista. |
Namnet återkommer trots DISTINCT på namn och stad | Personerna har olika städer, så kombinationerna är unika | Välj bara namn om det är just namnen du vill deduplicera. |
| En kund upprepas efter en koppling till ordrar | Kunden har flera matchande orderrader | Kontrollera kopplingsvillkoret eller välj endast kundfältet med DISTINCT. |
Resultatet innehåller en NULL-rad | Det finns poster utan känt värde | Filtrera med IS NOT NULL om den raden inte ska visas. |
| Du vill ta bort faktiska dubbletter ur tabellen | DISTINCT påverkar bara frågans resultat | Hantera datarensningen separat och kontrollera vilka poster som ska behållas. |
Kontrollera först vilka kolumner frågan faktiskt returnerar och om en koppling skapar flera träffar. Det brukar visa om du behöver en unik lista eller om själva frågelogiken behöver ändras.
PostgreSQL kan välja en rad per grupp med DISTINCT ON
Ibland vill du få en rad per kund, men behålla ordern med det senaste datumet. Vanlig DISTINCT löser inte valet av representativ rad på egen hand. PostgreSQL har den särskilda syntaxen DISTINCT ON för det ändamålet:
SELECT DISTINCT ON (kund_id)
kund_id, orderdatum, order_id
FROM ordrar
ORDER BY kund_id, orderdatum DESC, order_id DESC;Sorteringen gör att den senaste ordern hamnar först inom varje kundgrupp; order_id ger en extra skiljeregel om flera ordrar har samma datum. PostgreSQL-dokumentationen påpekar att den valda första raden annars kan bli oförutsägbar om sorteringen inte räcker för att avgöra ordningen.
DISTINCT ON är PostgreSQL-specifikt och ingår inte i SQL-standarden. Om frågan ska fungera i flera databassystem behöver du i stället överväga en lösning med exempelvis fönsterfunktionen ROW_NUMBER().
En kort kontroll gör frågan lättare att lita på
När resultatet ser fel ut, lägg till en kolumn i taget och se när extra rader dyker upp. Om du vill ha en rad per land, välj bara landet; om du vill visa land och språk, acceptera att samma land kan förekomma flera gånger.
Den kontrollen fångar ofta den vanligaste feltolkningen: DISTINCT gör valda rader unika som helhet. Det är den skillnaden som avgör om rapporten blir en ren lista eller en lista med unika kombinationer.