Oto siedem opcji znajdowania zduplikowanych wierszy w SQL Server, gdy te wiersze mają klucz podstawowy lub inną kolumnę unikatowego identyfikatora.
Innymi słowy, tabela zawiera co najmniej dwa wiersze, które mają dokładnie te same wartości we wszystkich kolumnach, z wyjątkiem kolumny z unikalnym identyfikatorem.
Przykładowe dane
Załóżmy, że mamy tabelę z następującymi danymi:
SELECT * FROM Dogs;
Wynik:
+---------+-------------+------------+ | DogId | FirstName | LastName | |---------+-------------+------------| | 1 | Bark | Smith | | 2 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +---------+-------------+------------+
Widzimy, że pierwsze dwa wiersze są duplikatami (z wyjątkiem DogId
kolumna, która zawiera unikatową wartość we wszystkich wierszach i może być używana jako kolumna klucza podstawowego tabeli). Widzimy również, że ostatnie trzy wiersze są duplikatami (z wyjątkiem DogId
kolumna).
Kolumna unikalnego identyfikatora zapewnia, że nie ma zduplikowanych wierszy, co jest zwykle bardzo pożądaną cechą w systemach RDBMS. Jednak w tym przypadku może to zakłócić naszą zdolność do znajdowania duplikatów. Z definicji kolumna unikalnego identyfikatora gwarantuje, że nie ma duplikatów. Na szczęście możemy dość łatwo rozwiązać ten problem, jak pokazują poniższe przykłady.
Opcja 1
Prawdopodobnie najłatwiejszym/najprostszym sposobem na to jest proste zapytanie, które używa GROUP BY
klauzula:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName;
Wynik:
+-------------+------------+---------+ | FirstName | LastName | Count | |-------------+------------+---------| | Wag | Johnson | 3 | | Woof | Jones | 1 | | Ruff | Robinson | 1 | | Bark | Smith | 2 | +-------------+------------+---------+
Udało nam się wykluczyć kolumnę klucza podstawowego/unikalnego identyfikatora, pomijając ją w naszym zapytaniu.
Wynik mówi nam, że istnieją trzy wiersze zawierające Wag Johnson i dwa wiersze zawierające Bark Smith. Są to duplikaty (lub trzy egzemplarze w przypadku Wag Johnson).
Opcja 2
Możemy wykluczyć nieduplikaty z wyniku, dołączając HAVING
klauzula w naszym zapytaniu:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1;
Wynik:
+-------------+------------+---------+ | FirstName | LastName | Count | |-------------+------------+---------| | Wag | Johnson | 3 | | Bark | Smith | 2 | +-------------+------------+---------+
Opcja 3
Możemy również sprawdzić duplikaty w połączonych kolumnach. Na przykład możemy użyć CONCAT()
funkcja łącząca nasze dwie kolumny:
SELECT
DISTINCT CONCAT(FirstName, ' ', LastName) AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY CONCAT(FirstName, ' ', LastName);
Wynik:
+---------------+---------+ | DogName | Count | |---------------+---------| | Bark Smith | 2 | | Ruff Robinson | 1 | | Wag Johnson | 3 | | Woof Jones | 1 | +---------------+---------+
Opcja 4
Możemy użyć ROW_NUMBER()
funkcja z PARTITION BY
klauzula, aby utworzyć nową kolumnę z numerem wiersza, który zwiększa się za każdym razem, gdy pojawia się duplikat, ale resetuje się ponownie, gdy istnieje unikalny wiersz:
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS Row_Number
FROM Dogs;
Wynik:
+---------+-------------+------------+--------------+ | DogId | FirstName | LastName | Row_Number | |---------+-------------+------------+--------------| | 1 | Bark | Smith | 1 | | 2 | Bark | Smith | 2 | | 4 | Ruff | Robinson | 1 | | 5 | Wag | Johnson | 1 | | 6 | Wag | Johnson | 2 | | 7 | Wag | Johnson | 3 | | 3 | Woof | Jones | 1 | +---------+-------------+------------+--------------+
Jedną z zalet tej metody jest to, że możemy zobaczyć każdy zduplikowany wiersz wraz z jego kolumną unikatowego identyfikatora, ponieważ nie grupujemy wyników.
Opcja 5
Możemy również użyć poprzedniego przykładu jako wspólnego wyrażenia tabelowego w większym zapytaniu:
WITH cte AS
(
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS Row_Number
FROM Dogs
)
SELECT * FROM cte WHERE Row_Number <> 1;
Wynik:
+---------+-------------+------------+--------------+ | DogId | FirstName | LastName | Row_Number | |---------+-------------+------------+--------------| | 2 | Bark | Smith | 2 | | 6 | Wag | Johnson | 2 | | 7 | Wag | Johnson | 3 | +---------+-------------+------------+--------------+
Ta opcja wyklucza z danych wyjściowych nieduplikaty.
Wyklucza również z danych wyjściowych dokładnie jeden wiersz każdego duplikatu. To otwiera nam drzwi do przekręcenia ostatniego SELECT *
w DELETE
aby usunąć duplikat tabeli, zachowując po jednym z każdego duplikatu.
Opcja 6
Oto bardziej zwięzły sposób na uzyskanie tego samego wyniku, co w poprzednim przykładzie:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
EXCEPT SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Wynik:
+-------+-----------+----------+ | DogId | FirstName | LastName | +-------+-----------+----------+ | 2 | Bark | Smith | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +-------+-----------+----------+
Ten przykład nie wymaga generowania własnego oddzielnego numeru wiersza.
Opcja 7
I na koniec, oto nieco bardziej skomplikowana technika zwracania zduplikowanych wierszy:
SELECT *
FROM Dogs d1, Dogs d2
WHERE d1.FirstName = d2.FirstName
AND d1.LastName = d2.LastName
AND d1.DogId <> d2.DogId
AND d1.DogId = (
SELECT MAX(DogId)
FROM Dogs d3
WHERE d3.FirstName = d1.FirstName
AND d3.LastName = d1.LastName
);
Wynik:
+---------+-------------+------------+---------+-------------+------------+ | DogId | FirstName | LastName | DogId | FirstName | LastName | |---------+-------------+------------+---------+-------------+------------| | 2 | Bark | Smith | 1 | Bark | Smith | | 7 | Wag | Johnson | 5 | Wag | Johnson | | 7 | Wag | Johnson | 6 | Wag | Johnson | +---------+-------------+------------+---------+-------------+------------+
Nawet wynik wygląda na bardziej zawiły, ale hej, nadal pokazuje nam duplikaty!