DISTINCT usuwa zduplikowane wiersze
Domyślnie SELECT zwraca każdy pasujący wiersz, razem z duplikatami. DISTINCT każe SQLite połączyć wiersze identyczne we wszystkich wybranych kolumnach, tak aby każda unikalna kombinacja pojawiła się tylko raz.
Pięć wierszy na wejściu, trzy na wyjściu. SQLite przejrzał kolumnę customer, wyrzucił powtórzenia i zwrócił jeden wiersz na każdą unikalną wartość. Kolejność nie jest gwarantowana: dodaj ORDER BY, jeśli ma dla ciebie znaczenie.
DISTINCT dotyczy całej listy wyboru
Na tym ludzie się potykają. DISTINCT nie wybiera jednej kolumny do usuwania duplikatów, tylko usuwa zduplikowane całe wiersze na podstawie wszystkich wybranych kolumn.
Każda unikalna para (customer, country) pojawia się raz. Gdyby ten sam klient wystąpił z dwoma różnymi krajami, zobaczysz oba wiersze, bo dla SQLite nie są duplikatami.
Nie ma składni DISTINCT(customer), która ignorowałaby pozostałe kolumny. Nawiasy kuszą, ale SELECT DISTINCT(customer), country jest parsowane tak samo jak SELECT DISTINCT customer, country: nawiasy tylko grupują wyrażenie. Jeśli naprawdę chcesz jeden wiersz na klienta z jakimś wybranym krajem, to zadanie dla GROUP BY z funkcją agregującą.
COUNT(DISTINCT col)
Częsta potrzeba: ile unikalnych wartości jest w kolumnie? COUNT(*) liczy wiersze, COUNT(col) liczy wartości różne od NULL, a COUNT(DISTINCT col) liczy unikalne wartości różne od NULL.
Pięć zamówień, trzech unikalnych klientów, trzy unikalne kraje. COUNT(DISTINCT ...) to najbardziej przydatna forma DISTINCT w agregacji: sięgniesz po nią za każdym razem, gdy chcesz policzyć, "ile różnych rzeczy się pojawiło".
Zwróć uwagę, że SQLite pozwala tylko na jedną kolumnę wewnątrz COUNT(DISTINCT ...). Aby policzyć unikalne kombinacje kilku kolumn, opakuj je w podzapytanie: SELECT COUNT(*) FROM (SELECT DISTINCT a, b FROM t).
Jak DISTINCT traktuje NULL
NULL ma w SQL dziwaczną reputację, bo NULL = NULL daje NULL, a nie TRUE. Ale DISTINCT robi specjalny wyjątek: przy usuwaniu duplikatów wszystkie NULL są uważane za równe sobie.
Wracają trzy wiersze: 'ada@example.com', 'dan@example.com' i jedno NULL. Trzy adresy NULL zlały się w jeden. Ta sama reguła dotyczy GROUP BY i operacji na zbiorach, takich jak UNION. Warto o tym pamiętać, gdy szukasz odpowiedzi na pytanie "dlaczego ten wiersz z NULL pojawia się raz, a nie trzy razy?".
DISTINCT działa przed ORDER BY i LIMIT
Klauzule w SELECT mają logiczną kolejność: FROM → WHERE → GROUP BY → HAVING → SELECT/DISTINCT → ORDER BY → LIMIT. Dlatego DISTINCT najpierw usuwa duplikaty, potem ORDER BY sortuje to, co zostało, a na końcu LIMIT przycina wynik.
WHERE zostawia cztery wiersze, DISTINCT łączy duplikaty Borisa, ORDER BY sortuje alfabetycznie, a LIMIT zwraca pierwsze dwa. Warto raz prześledzić to krok po kroku: nieporozumienia co do kolejności wyników zwykle biorą się z zapomnienia, który krok następuje kiedy.
DISTINCT a GROUP BY
Przy samym usuwaniu duplikatów te dwa zapytania zwracają te same wiersze:
Ten sam wynik. Różnica polega na tym, co możesz zrobić dalej:
DISTINCTsłuży do "daj mi unikalne wiersze" i niczego więcej.GROUP BYsłuży do "podziel wiersze na koszyki i policz coś dla każdego":COUNT(*),SUM(amount),MAX(created_at)i tak dalej.
Jeśli sięgasz po DISTINCT, a potem orientujesz się, że chcesz też sumy dla każdego klienta, to sygnał, żeby przejść na GROUP BY:
Jeden wiersz na klienta z potrzebnymi agregatami. DISTINCT by tego nie zrobił, bo nie potrafi wyrazić "jeden wiersz na grupę i suma".
Na co uważać
- Wydajność.
DISTINCTzwykle wymaga od SQLite posortowania lub zahaszowania wierszy, żeby znaleźć duplikaty. Przy dużych wynikach pomaga indeks na kolumnach, z których usuwasz duplikaty. Jeśli robiszSELECT DISTINCTna każdej kolumnie szerokiej tabeli, zastanów się, czy naprawdę potrzebujesz wszystkich kolumn. DISTINCT *to rzadkość. Jest dozwolone (SELECT DISTINCT * FROM tusuwa zduplikowane całe wiersze), ale jeśli tabela ma klucz główny, każdy wiersz i tak jest unikalny, więc nic to nie daje.- Nie myl z
UNIQUE.UNIQUEto ograniczenie tabeli, które w ogóle nie pozwala wstawić zduplikowanych wartości.DISTINCTto filtr w czasie zapytania, który ukrywa duplikaty w wyniku. Różne narzędzia do różnych zadań.
Dalej: wyrażenia CASE
Gdy potrafisz już kształtować wiersze wyniku za pomocą SELECT, WHERE, ORDER BY i DISTINCT, kolejnym krokiem jest logika warunkowa wewnątrz zapytania. Wyrażenia CASE pozwalają zwracać różne wartości w zależności od warunków. To SQL-owy odpowiednik drabinki if/else, a omawia je następna strona.
Najczęściej zadawane pytania
Jak działa SELECT DISTINCT w SQLite?
SELECT DISTINCT usuwa zduplikowane wiersze z wyniku. SQLite porównuje każdą kolumnę z listy wyboru i zostawia jeden wiersz na każdą unikalną kombinację. Działa po WHERE i JOIN, ale przed ORDER BY i LIMIT.
Czy w SQLite mogę użyć DISTINCT na kilku kolumnach?
Tak, DISTINCT zawsze dotyczy całej listy wyboru, a nie pojedynczej kolumny. SELECT DISTINCT city, country FROM users zwraca każdą unikalną parę (city, country). Nie ma składni DISTINCT(city), która ignorowałaby pozostałe kolumny. Jeśli tego potrzebujesz, użyj GROUP BY z funkcją agregującą.
Jak DISTINCT traktuje wartości NULL w SQLite?
Przy usuwaniu duplikatów DISTINCT traktuje NULL jako równe innym NULL, więc wiele wierszy z NULL zlewa się w jeden. To inaczej niż przy = w klauzulach WHERE, gdzie NULL = NULL daje wynik nieznany. To specjalna reguła tylko dla DISTINCT, GROUP BY i UNION.
Czym różni się DISTINCT od GROUP BY w SQLite?
Przy samym usuwaniu duplikatów SELECT DISTINCT col i SELECT col FROM t GROUP BY col dają identyczne wyniki. Różnica tkwi w intencji: używaj DISTINCT, gdy chcesz tylko unikalnych wierszy, a GROUP BY, gdy chcesz też policzyć agregaty, takie jak COUNT(*) czy SUM(amount), dla każdej grupy.