Aqui estão sete maneiras de retornar linhas duplicadas no MySQL quando essas linhas têm uma chave primária ou outra coluna de identificador exclusivo.
Dados de amostra
Usaremos os seguintes dados para nossos exemplos:
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;
Resultado:
+-------+-----------+----------+ | DogId | FirstName | LastName | +-------+-----------+----------+ | 1 | Bark | Smith | | 2 | Bark | Smith | | 3 | Woof | Jones | | 4 | Ruff | Robinson | | 5 | Wag | Johnson | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +-------+-----------+----------+
As linhas duplicadas compartilham exatamente os mesmos valores em todas as colunas, exceto na coluna de chave primária/ID exclusivo.
As duas primeiras linhas são duplicadas (exceto para o
DogId
coluna, que é a chave primária da tabela e contém um valor exclusivo em todas as linhas). As últimas três linhas também são duplicadas (exceto para o DogId
coluna). A coluna de chave primária garante que não haja linhas duplicadas, o que normalmente é bom em RDBMSs. No entanto, por definição, isso significa que não há duplicatas. No nosso caso, a coluna de chave primária é um número incrementado e seu valor não tem significado e não é significativo. Portanto, precisamos ignorar essa linha se quisermos encontrar duplicatas nas colunas que são significativo.
Opção 1
Nossa primeira opção é usar o
GROUP BY
cláusula para agrupar as colunas por suas colunas significativas, então use o COUNT()
função para retornar o número de linhas idênticas:SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName;
Resultado:
+-----------+----------+-------+ | FirstName | LastName | Count | +-----------+----------+-------+ | Bark | Smith | 2 | | Woof | Jones | 1 | | Ruff | Robinson | 1 | | Wag | Johnson | 3 | +-----------+----------+-------+
Conseguimos ignorar a coluna de chave primária omitindo-a de nossa consulta.
O resultado nos diz que existem duas linhas contendo Bark Smith e três linhas contendo Wag Johnson. Estes são duplicados (ou triplicados no caso de Wag Johnson). As outras duas linhas não têm duplicatas.
Opção 2
Podemos excluir não duplicados da saída com o
HAVING
cláusula:SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1;
Resultado:
+-----------+----------+-------+ | FirstName | LastName | Count | +-----------+----------+-------+ | Bark | Smith | 2 | | Wag | Johnson | 3 | +-----------+----------+-------+
Opção 3
Também podemos verificar duplicatas em colunas concatenadas. Por exemplo, podemos usar o
CONCAT()
função para concatenar nossas duas colunas, use a função DISTINCT
palavra-chave para obter valores distintos, então use o COUNT()
função para retornar a contagem:SELECT
DISTINCT CONCAT(FirstName, ' ', LastName) AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY CONCAT(FirstName, ' ', LastName);
Resultado:
+---------------+-------+ | DogName | Count | +---------------+-------+ | Bark Smith | 2 | | Woof Jones | 1 | | Ruff Robinson | 1 | | Wag Johnson | 3 | +---------------+-------+
Opção 4
Alternativamente, podemos usar o
ROW_NUMBER()
função com a função PARTITION BY
cláusula:SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS rn
FROM Dogs;
Resultado:
+-------+-----------+----------+----+ | 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 | +-------+-----------+----------+----+
Isso cria uma nova coluna com um número de linha que aumenta cada vez que há uma duplicata, mas reinicia novamente quando há uma linha única
Essa técnica fornece um possível benefício, pois não precisamos agrupar os resultados. Isso significa que podemos ver cada linha duplicada, incluindo sua coluna de identificador exclusivo.
Opção 5
Podemos usar o exemplo anterior como uma expressão de tabela comum em uma consulta maior:
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;
Resultado:
+-------+-----------+----------+----+ | DogId | FirstName | LastName | rn | +-------+-----------+----------+----+ | 2 | Bark | Smith | 2 | | 6 | Wag | Johnson | 2 | | 7 | Wag | Johnson | 3 | +-------+-----------+----------+----+
Essa técnica exclui não duplicados da saída e exclui uma linha de cada duplicata da saída.
Essa consulta pode ser usada como precursora de uma operação de desduplicação. Ele pode nos mostrar o que será excluído se decidirmos excluir duplicatas. Para desduplicar a tabela, tudo o que precisamos fazer é substituir o último
SELECT *
com DELETE
. Opção 6
Aqui está uma maneira mais concisa de obter a mesma saída do exemplo anterior:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
WHERE DogId NOT IN (SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName)
);
Resultado:
+-------+-----------+----------+ | DogId | FirstName | LastName | +-------+-----------+----------+ | 2 | Bark | Smith | | 6 | Wag | Johnson | | 7 | Wag | Johnson | +-------+-----------+----------+
Essa técnica não exige que geremos um número de linha separado com
ROW_NUMBER()
como no exemplo anterior. Também podemos substituir
SELECT *
com DELETE
para excluir as duplicatas. Opção 7
E, finalmente, aqui está mais uma opção para retornar duplicatas:
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
);
Resultado:
+-------+-----------+----------+-------+-----------+----------+ | DogId | FirstName | LastName | DogId | FirstName | LastName | +-------+-----------+----------+-------+-----------+----------+ | 2 | Bark | Smith | 1 | Bark | Smith | | 7 | Wag | Johnson | 5 | Wag | Johnson | | 7 | Wag | Johnson | 6 | Wag | Johnson | +-------+-----------+----------+-------+-----------+----------+