Aqui estão quatro métodos que você pode usar para localizar linhas duplicadas no SQL Server.
Por “linhas duplicadas” quero dizer duas ou mais linhas que compartilham exatamente os mesmos valores em todas as colunas.
Dados de amostra
Suponha que temos uma tabela com os seguintes dados:
SELECT * FROM Pets;
Resultado:
+---------+-----------+-----------+ | PetId | PetName | PetType | |---------+-----------+-----------| | 1 | Wag | Dog | | 1 | Wag | Dog | | 2 | Scratch | Cat | | 3 | Tweet | Bird | | 4 | Bark | Dog | | 4 | Bark | Dog | | 4 | Bark | Dog | +---------+-----------+-----------+
Podemos ver que as duas primeiras linhas são duplicadas, assim como as três últimas linhas.
Opção 1
Podemos usar a seguinte consulta para retornar informações sobre linhas duplicadas:
SELECT
DISTINCT PetId,
COUNT(*) AS "Count"
FROM Pets
GROUP BY PetId
ORDER BY PetId;
Resultado:
+---------+---------+ | PetId | Count | |---------+---------| | 1 | 2 | | 2 | 1 | | 3 | 1 | | 4 | 3 | +---------+---------+
Podemos expandir o
SELECT
list para incluir mais colunas, se necessário:SELECT
PetId,
PetName,
PetType,
COUNT(*) AS "Count"
FROM Pets
GROUP BY
PetId,
PetName,
PetType
ORDER BY PetId;
Resultado:
+---------+-----------+-----------+---------+ | PetId | PetName | PetType | Count | |---------+-----------+-----------+---------| | 1 | Wag | Dog | 2 | | 2 | Scratch | Cat | 1 | | 3 | Tweet | Bird | 1 | | 4 | Bark | Dog | 3 | +---------+-----------+-----------+---------+
Se a tabela tiver um identificador exclusivo, podemos simplesmente remover essa coluna da consulta. Por exemplo, se assumirmos que o
PetId
column é na verdade uma coluna de chave primária que contém um ID exclusivo, poderíamos executar a seguinte consulta para retornar todas as linhas duplicadas, sem contar a coluna de chave primária:SELECT
PetName,
PetType,
COUNT(*) AS "Count"
FROM Pets
GROUP BY
PetName,
PetType
ORDER BY PetName;
Resultado:
+-----------+-----------+---------+ | PetName | PetType | Count | |-----------+-----------+---------| | Bark | Dog | 3 | | Scratch | Cat | 1 | | Tweet | Bird | 1 | | Wag | Dog | 2 | +-----------+-----------+---------+
Opção 2
Se quisermos apenas as linhas duplicadas reais retornadas, podemos adicionar o
HAVING
cláusula:SELECT
PetId,
PetName,
PetType,
COUNT(*) AS "Count"
FROM Pets
GROUP BY
PetId,
PetName,
PetType
HAVING COUNT(*) > 1
ORDER BY PetId;
Resultado:
+---------+-----------+-----------+---------+ | PetId | PetName | PetType | Count | |---------+-----------+-----------+---------| | 1 | Wag | Dog | 2 | | 4 | Bark | Dog | 3 | +---------+-----------+-----------+---------+
Opção 3
Outra maneira de fazer isso é usar o
ROW_NUMBER()
função com a função PARTITION BY
cláusula para numerar a saída do conjunto de resultados. SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY PetId, PetName, PetType
ORDER BY PetId, PetName, PetType
) AS Row_Number
FROM Pets;
Resultado:
+---------+-----------+-----------+--------------+ | PetId | PetName | PetType | Row_Number | |---------+-----------+-----------+--------------| | 1 | Wag | Dog | 1 | | 1 | Wag | Dog | 2 | | 2 | Scratch | Cat | 1 | | 3 | Tweet | Bird | 1 | | 4 | Bark | Dog | 1 | | 4 | Bark | Dog | 2 | | 4 | Bark | Dog | 3 | +---------+-----------+-----------+--------------+
A
PARTITION BY
cláusula divide o conjunto de resultados produzido pelo FROM
cláusula em partições às quais a função é aplicada. Quando especificamos partições para o conjunto de resultados, cada partição faz com que a numeração comece novamente (ou seja, a numeração começará em 1 para a primeira linha de cada partição). Opção 4
Se quisermos que apenas as linhas excedentes das duplicatas correspondentes sejam retornadas, podemos usar a consulta acima como uma expressão de tabela comum, assim:
WITH CTE AS
(
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY PetId, PetName, PetType
ORDER BY PetId, PetName, PetType
) AS Row_Number
FROM Pets
)
SELECT * FROM CTE WHERE Row_Number <> 1;
Resultado:
+---------+-----------+-----------+--------------+ | PetId | PetName | PetType | Row_Number | |---------+-----------+-----------+--------------| | 1 | Wag | Dog | 2 | | 4 | Bark | Dog | 2 | | 4 | Bark | Dog | 3 | +---------+-----------+-----------+--------------+
Um dos benefícios de fazer isso é que podemos excluir linhas duplicadas simplesmente alternando
SELECT *
para DELETE
(na última linha). Portanto, podemos usar o código acima para ver quais linhas serão excluídas e, quando estivermos convencidos de que vamos excluir as linhas corretas, podemos alterná-lo para um
DELETE
instrução para realmente excluí-los. Assim:
WITH CTE AS
(
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY PetId, PetName, PetType
ORDER BY PetId, PetName, PetType
) AS Row_Number
FROM Pets
)
DELETE FROM CTE WHERE Row_Number <> 1;