programowanieAnalityk SQL

W jaki sposób zrealizować selekcję tylko unikalnych rekordów z złożonej struktury danych z wieloma duplikatami w różnych kolumnach, oraz jaka jest specyfika działania DISTINCT vs GROUP BY vs ROW_NUMBER()?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

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:

  • Użyj DISTINCT, aby uzyskać unikalne wiersze na podstawie wymaganych pól.
  • GROUP BY — gdy potrzebne są agregaty (np. suma lub data na podstawie unikalnych par).
  • Funkcje okienne (ROW_NUMBER()) — dla zadań typu selekcja "jednego wiersza z grupy duplikatów na podstawie jakiegoś kryterium".

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:

  • DISTINCT — zwraca unikalne wiersze tylko na podstawie tych pól, które zostały podane w SELECT.
  • GROUP BY — jest wymagany, jeśli potrzebna jest agregacja.
  • ROW_NUMBER() — maksymalnie elastyczny do selekcji wierszy z odpowiednim priorytetem/datą/wersjonowaniem.

Pytania podchwytliwe.

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.

Typowe błędy i antywzorce

  • Użycie DISTINCT przy dużej liczbie kolumn i wierszy — nagły spadek wydajności.
  • GROUP BY bez potrzebnej agregacji — bezsensowne i kosztowne w zasobach.
  • Funkcje okienne bez późniejszego filtru — dane zwracane z powtórzeniami.

Przykład z życia

Negatywny przypadek

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:

  • Łatwo to napisać.

Wady:

  • Skrajnie nieefektywne na dużych danych.

Pozytywny przypadek

Użyto funkcji okiennych: uzyskano tylko potrzebny ostatni rekord na klienta w milisekundy; wcześniejsze i powtarzające się nie były ładowane.

Zalety:

  • Najwyższa wydajność.
  • Elastyczność.

Wady:

  • Wymaga umiejętności architektury zapytania i wiedzy o funkcjach okiennych.