Här är sju sätt att returnera dubbletter av rader i MySQL när dessa rader har en primärnyckel eller annan unik identifierarkolumn.
Exempeldata
Vi använder följande data för våra exempel:
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 | +-------+-----------+----------+
Dubblettraderna delar exakt samma värden i alla kolumner förutom deras primärnyckel/unika ID-kolumn.
De två första raderna är dubbletter (förutom DogId
kolumn, som är tabellens primärnyckel och innehåller ett unikt värde över alla rader). De sista tre raderna är också dubbletter (förutom DogId
kolumn).
Den primära nyckelkolumnen säkerställer att det inte finns några dubbletter av rader, vilket normalt är bra i RDBMS. Men per definition innebär detta att det inte finns några dubbletter. I vårt fall är primärnyckelkolumnen ett ökande tal, och dess värde har ingen betydelse och är inte signifikant. Vi måste därför ignorera den raden om vi vill hitta dubbletter i kolumnerna som är signifikant.
Alternativ 1
Vårt första alternativ är att använda GROUP BY
sats för att gruppera kolumnerna efter deras signifikanta kolumner, använd sedan COUNT()
funktion för att returnera antalet identiska rader:
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 kunde ignorera primärnyckelkolumnen genom att utelämna den från vår fråga.
Resultatet berättar att det finns två rader som innehåller Bark Smith och tre rader som innehåller Wag Johnson. Dessa är dubbletter (eller triplikat i fallet med Wag Johnson). De andra två raderna har inga dubbletter.
Alternativ 2
Vi kan utesluta icke-duplikat från utdata 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 | +-----------+----------+-------+
Alternativ 3
Vi kan också kontrollera om det finns dubbletter på sammanlänkade kolumner. Till exempel kan vi använda CONCAT()
funktion för att sammanfoga våra två kolumner, använd DISTINCT
nyckelord för att få distinkta värden, använd sedan COUNT()
funktion för att returnera antalet:
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 | +---------------+-------+
Alternativ 4
Vi kan alternativt använda 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 | +-------+-----------+----------+----+
Detta skapar en ny kolumn med ett radnummer som ökar varje gång det finns en dubblett, men återställs igen när det finns en unik rad
Denna teknik ger en möjlig fördel genom att vi inte behöver gruppera resultaten. Det betyder att vi kan se varje dubblettrad, inklusive dess unika identifierarkolumn.
Alternativ 5
Vi kan använda föregående exempel som ett vanligt tabelluttryck i en större fråga:
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 | +-------+-----------+----------+----+
Den här tekniken utesluter icke-dubbletter från utdata, och den exkluderar en rad av varje duplikat från utdata.
Denna fråga kan användas som en föregångare till en de-dupingoperation. Det kan visa oss vad som kommer att raderas om vi bestämmer oss för att ta bort dubbletter. För att de-dupera tabellen behöver vi bara ersätta den sista SELECT *
med DELETE
.
Alternativ 6
Här är ett mer kortfattat sätt att få samma utdata som föregående exempel:
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 | +-------+-----------+----------+
Den här tekniken kräver inte att vi genererar ett separat radnummer med ROW_NUMBER()
som i föregående exempel.
Vi kan också ersätta SELECT *
med DELETE
för att ta bort dubbletterna.
Alternativ 7
Och slutligen, här är ytterligare ett alternativ för att returnera dubbletter:
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 | +-------+-----------+----------+-------+-----------+----------+