Wydobycie unikalnych rekordów w SQL stało się krytycznie ważnym zadaniem w wyniku masowego przejścia organizacji na przechowywanie wielowymiarowych danych. Czasami konieczne jest wyświetlenie unikalnych wierszy na podstawie kombinacji kilku kolumn, czasami — jedynie na podstawie klucza.
Historia pytania:
Pierwsze wersje SQL oferowały tylko DISTINCT do filtrowania duplikatów. Następnie pojawiły się strukturalne techniki, w tym GROUP BY do agregacji na podstawie unikalnych zestawów wartości oraz funkcje okienne, takie jak ROW_NUMBER(), do bardziej elastycznych scenariuszy pracy z duplikatami, na przykład: selekcja według "ostatniego" lub "pierwszego" rekordu.
Problem:
DISTINCT działa tylko na poziomie zestawu pól w SELECT, podczas gdy GROUP BY wymaga agregacji. Funkcje okienne pozwalają na zaawansowaną logikę, ale ich użycie często prowadzi do błędów, jeśli nie przemyśli się kolejności wybierania wierszy. Często deweloperzy mylą te podejścia, co prowadzi do błędnych wyników.
Rozwiązanie:
Przykład kodu:
Uzyskać jeden ostatni rekord o zamówieniach dla każdego klienta:
WITH OrdersRank AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate DESC) as rn FROM Orders ) SELECT * FROM OrdersRank WHERE rn = 1;
Kluczowe cechy:
Czy można używać DISTINCT razem z funkcjami agregacyjnymi bez GROUP BY?
Nie, funkcje agregacyjne wymagają grupowania, w przeciwnym razie wystąpi błąd składni.
SELECT COUNT(DISTINCT CustomerID) -- poprawnie SELECT SUM(Amount), DISTINCT CustomerID -- błąd!
Co się stanie, jeśli w GROUP BY nie wskaź wszystkie nieagregowane pola z SELECT?
Spowoduje to błąd w większości DBMS: wszystkie pola w SELECT, z wyjątkiem agregacyjnych, muszą być wymienione w GROUP BY.
Czy można "usunąć" duplikaty za pomocą funkcji okiennych bez podzapytania?
Nie: użycie ROW_NUMBER() w jednym SELECT nie filtruje automatycznie „powtórzeń”, potrzebne jest zewnętrzne zapytanie do wybierania odpowiednich wierszy.
Wybrano DISTINCT dla wszystkich kolumn w tabeli z 20 mln wierszy: zapytanie działało przez godziny, wynik — timeout lub spadek wydajności bazy danych.
Zalety:
Wady:
Użyto funkcji okiennych: uzyskano tylko potrzebny ostatni rekord na klienta w milisekundy; wcześniejsze i powtarzające się nie były ładowane.
Zalety:
Wady: