Her er syv måder at returnere duplikerede rækker i MySQL, når disse rækker har en primær nøgle eller en anden unik identifikatorkolonne.
Eksempeldata
Vi bruger følgende data til vores eksempler:
DROP TABLE IF EXISTS Dogs;
CREATE TABLE Dogs (
DogId int PRIMARY KEY NOT NULL,
FirstName varchar(50),
LastName varchar(50)
);
INSERT INTO Dogs VALUES
(1, 'Bark', 'Smith'),
(2, 'Bark', 'Smith'),
(3, 'Woof', 'Jones'),
(4, 'Ruff', 'Robinson'),
(5, 'Wag', 'Johnson'),
(6, 'Wag', 'Johnson'),
(7, 'Wag', 'Johnson');
SELECT * FROM Dogs;
Resultat:
+-------+-----------+----------+ | DogId | FirstName | LastName | +-------+-----------+----------+ | 1 | Bark | Smith | | 2 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +-------+-----------+----------+
De dublerede rækker deler nøjagtigt de samme værdier på tværs af alle kolonner undtagen deres primære nøgle/unikke id-kolonne.
De første to rækker er dubletter (undtagen DogId
kolonne, som er tabellens primære nøgle og indeholder en unik værdi på tværs af alle rækker). De sidste tre rækker er også dubletter (bortset fra DogId
kolonne).
Den primære nøglekolonne sikrer, at der ikke er nogen duplikerede rækker, hvilket normalt er en god ting i RDBMS'er. Men per definition betyder dette, at der ikke er nogen dubletter. I vores tilfælde er den primære nøglekolonne et stigende tal, og dens værdi har ingen betydning og er ikke signifikant. Vi er derfor nødt til at ignorere den række, hvis vi vil finde dubletter i de kolonner, der er væsentlig.
Valgmulighed 1
Vores første mulighed er at bruge GROUP BY
klausul for at gruppere kolonnerne efter deres signifikante kolonner, og brug derefter COUNT()
funktion for at returnere antallet af identiske rækker:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName;
Resultat:
+-----------+----------+-------+ | FirstName | LastName | Count | +-----------+----------+-------+ | Bark | Smith | 2 | | Woof | Jones | 1 | | Ruff | Robinson | 1 | | Wag | Johnson | 3 | +-----------+----------+-------+
Vi var i stand til at ignorere den primære nøglekolonne ved at udelade den fra vores forespørgsel.
Resultatet fortæller os, at der er to rækker indeholdende Bark Smith og tre rækker indeholdende Wag Johnson. Disse er dubletter (eller triplikater i tilfældet med Wag Johnson). De to andre rækker har ingen dubletter.
Valgmulighed 2
Vi kan ekskludere ikke-duplikater fra outputtet med HAVING
klausul:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1;
Resultat:
+-----------+----------+-------+ | FirstName | LastName | Count | +-----------+----------+-------+ | Bark | Smith | 2 | | Wag | Johnson | 3 | +-----------+----------+-------+
Valgmulighed 3
Vi kan også kontrollere for dubletter på sammenkædede kolonner. For eksempel kan vi bruge CONCAT()
funktion til at sammenkæde vores to kolonner, brug DISTINCT
søgeord for at få forskellige værdier, og brug derefter COUNT()
funktion for at returnere antallet:
SELECT
DISTINCT CONCAT(FirstName, ' ', LastName) AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY CONCAT(FirstName, ' ', LastName);
Resultat:
+---------------+-------+ | DogName | Count | +---------------+-------+ | Bark Smith | 2 | | Woof Jones | 1 | | Ruff Robinson | 1 | | Wag Johnson | 3 | +---------------+-------+
Valgmulighed 4
Vi kan alternativt bruge ROW_NUMBER()
funktion med PARTITION BY
klausul:
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS rn
FROM Dogs;
Resultat:
+-------+-----------+----------+----+ | DogId | FirstName | LastName | rn | +-------+-----------+----------+----+ | 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 | +-------+-----------+----------+----+
Dette opretter en ny kolonne med et rækkenummer, der stiger hver gang der er en dublet, men nulstilles igen, når der er en unik række
Denne teknik giver en mulig fordel ved, at vi ikke behøver at gruppere resultaterne. Det betyder, at vi kan se hver dubletrække, inklusive dens unikke identifikatorkolonne.
Valgmulighed 5
Vi kan bruge det foregående eksempel som et almindeligt tabeludtryk i en større forespørgsel:
WITH cte AS
(
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS rn
FROM Dogs
)
SELECT * FROM cte WHERE rn <> 1;
Resultat:
+-------+-----------+----------+----+ | DogId | FirstName | LastName | rn | +-------+-----------+----------+----+ | 2 | Bark | Smith | 2 | | 6 | Wag | Johnson | 2 | | 7 | Wag | Johnson | 3 | +-------+-----------+----------+----+
Denne teknik udelukker ikke-duplikater fra outputtet, og den udelukker en række af hver dublet fra outputtet.
Denne forespørgsel kan bruges som en forløber for en de-duping-operation. Det kan vise os, hvad der vil blive slettet, hvis vi beslutter os for at slette dubletter. For at de-dupere tabellen er alt, hvad vi skal gøre, at erstatte den sidste SELECT *
med DELETE
.
Valgmulighed 6
Her er en mere kortfattet måde at få det samme output som det forrige eksempel:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
WHERE DogId NOT IN (SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName)
);
Resultat:
+-------+-----------+----------+ | DogId | FirstName | LastName | +-------+-----------+----------+ | 2 | Bark | Smith | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +-------+-----------+----------+
Denne teknik kræver ikke, at vi genererer et separat rækkenummer med ROW_NUMBER()
ligesom i det foregående eksempel.
Vi kan også erstatte SELECT *
med DELETE
for at slette dubletterne.
Valgmulighed 7
Og endelig, her er endnu en mulighed for at returnere dubletter:
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
);
Resultat:
+-------+-----------+----------+-------+-----------+----------+ | DogId | FirstName | LastName | DogId | FirstName | LastName | +-------+-----------+----------+-------+-----------+----------+ | 2 | Bark | Smith | 1 | Bark | Smith | | 7 | Wag | Johnson | 5 | Wag | Johnson | | 7 | Wag | Johnson | 6 | Wag | Johnson | +-------+-----------+----------+-------+-----------+----------+