Hier zijn zeven manieren om dubbele rijen in MySQL te retourneren wanneer die rijen een primaire sleutel of een andere unieke identificatiekolom hebben.
Voorbeeldgegevens
We gebruiken de volgende gegevens voor onze voorbeelden:
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;
Resultaat:
+-------+-----------+----------+ | 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 dubbele rijen delen exact dezelfde waarden in alle kolommen, behalve hun primaire sleutel/unieke ID-kolom.
De eerste twee rijen zijn duplicaten (behalve de DogId
kolom, de primaire sleutel van de tabel, en bevat een unieke waarde voor alle rijen). De laatste drie rijen zijn ook duplicaten (behalve de DogId
kolom).
De primaire sleutelkolom zorgt ervoor dat er geen dubbele rijen zijn, wat normaal gesproken een goede zaak is in RDBMS'en. Dit betekent echter per definitie dat er geen duplicaten zijn. In ons geval is de primaire sleutelkolom een oplopend getal en de waarde ervan heeft geen betekenis en is niet significant. We moeten die rij daarom negeren als we duplicaten willen vinden in de kolommen die zijn significant.
Optie 1
Onze eerste optie is om de GROUP BY
. te gebruiken clausule om de kolommen te groeperen op hun significante kolommen, gebruik dan de COUNT()
functie om het aantal identieke rijen terug te geven:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName;
Resultaat:
+-----------+----------+-------+ | FirstName | LastName | Count | +-----------+----------+-------+ | Bark | Smith | 2 | | Woof | Jones | 1 | | Ruff | Robinson | 1 | | Wag | Johnson | 3 | +-----------+----------+-------+
We konden de primaire sleutelkolom negeren door deze weg te laten uit onze zoekopdracht.
Het resultaat vertelt ons dat er twee rijen zijn met Bark Smith en drie rijen met Wag Johnson. Dit zijn duplicaten (of drievoud in het geval van Wag Johnson). De andere twee rijen hebben geen dubbele rijen.
Optie 2
We kunnen niet-duplicaten uitsluiten van de uitvoer met de HAVING
clausule:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1;
Resultaat:
+-----------+----------+-------+ | FirstName | LastName | Count | +-----------+----------+-------+ | Bark | Smith | 2 | | Wag | Johnson | 3 | +-----------+----------+-------+
Optie 3
We kunnen ook controleren op duplicaten op aaneengeschakelde kolommen. We kunnen bijvoorbeeld de CONCAT()
. gebruiken functie om onze twee kolommen samen te voegen, gebruik de DISTINCT
zoekwoord om verschillende waarden te krijgen, gebruik dan de COUNT()
functie om de telling terug te geven:
SELECT
DISTINCT CONCAT(FirstName, ' ', LastName) AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY CONCAT(FirstName, ' ', LastName);
Resultaat:
+---------------+-------+ | DogName | Count | +---------------+-------+ | Bark Smith | 2 | | Woof Jones | 1 | | Ruff Robinson | 1 | | Wag Johnson | 3 | +---------------+-------+
Optie 4
We kunnen ook de ROW_NUMBER()
. gebruiken functie met de PARTITION BY
clausule:
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS rn
FROM Dogs;
Resultaat:
+-------+-----------+----------+----+ | 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 | +-------+-----------+----------+----+
Hiermee wordt een nieuwe kolom gemaakt met een rijnummer dat wordt verhoogd telkens wanneer er een duplicaat is, maar opnieuw wordt ingesteld wanneer er een unieke rij is
Deze techniek biedt een mogelijk voordeel doordat we de resultaten niet hoeven te groeperen. Dit betekent dat we elke dubbele rij kunnen zien, inclusief de unieke identificatiekolom.
Optie 5
We kunnen het vorige voorbeeld gebruiken als een algemene tabeluitdrukking in een grotere query:
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;
Resultaat:
+-------+-----------+----------+----+ | DogId | FirstName | LastName | rn | +-------+-----------+----------+----+ | 2 | Bark | Smith | 2 | | 6 | Wag | Johnson | 2 | | 7 | Wag | Johnson | 3 | +-------+-----------+----------+----+
Deze techniek sluit niet-duplicaten uit van de uitvoer en sluit één rij van elk duplicaat uit van de uitvoer.
Deze query kan worden gebruikt als een voorloper van een ontdubbelingsoperatie. Het kan ons laten zien wat er wordt verwijderd als we besluiten om duplicaten te verwijderen. Om de tabel te ontdubbelen, hoeven we alleen de laatste SELECT *
. te vervangen met DELETE
.
Optie 6
Hier is een beknoptere manier om dezelfde uitvoer te krijgen als in het vorige voorbeeld:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
WHERE DogId NOT IN (SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName)
);
Resultaat:
+-------+-----------+----------+ | DogId | FirstName | LastName | +-------+-----------+----------+ | 2 | Bark | Smith | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +-------+-----------+----------+
Voor deze techniek hoeven we geen apart rijnummer te genereren met ROW_NUMBER()
zoals in het vorige voorbeeld.
We kunnen ook SELECT *
. vervangen met DELETE
om de duplicaten te verwijderen.
Optie 7
En tot slot, hier is nog een optie voor het retourneren van duplicaten:
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
);
Resultaat:
+-------+-----------+----------+-------+-----------+----------+ | DogId | FirstName | LastName | DogId | FirstName | LastName | +-------+-----------+----------+-------+-----------+----------+ | 2 | Bark | Smith | 1 | Bark | Smith | | 7 | Wag | Johnson | 5 | Wag | Johnson | | 7 | Wag | Johnson | 6 | Wag | Johnson | +-------+-----------+----------+-------+-----------+----------+