Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

7 maneiras de encontrar linhas duplicadas no SQL Server enquanto ignora qualquer chave primária


Aqui estão sete opções para localizar linhas duplicadas no SQL Server, quando essas linhas têm uma chave primária ou outra coluna de identificador exclusivo.

Em outras palavras, a tabela contém duas ou mais linhas que compartilham exatamente os mesmos valores em todas as colunas, exceto em sua coluna de identificador exclusivo.

Dados de amostra


Suponha que temos uma tabela com os seguintes dados:
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    |
+---------+-------------+------------+

Podemos ver que as duas primeiras linhas são duplicadas (exceto para o DogId coluna, que contém um valor exclusivo em todas as linhas e pode ser usada como a coluna de chave primária da tabela). Também podemos ver que as últimas três linhas são duplicadas (exceto para o DogId coluna).

A coluna de ID exclusiva garante que não haja linhas duplicadas, o que normalmente é uma característica altamente desejável em RDBMSs. No entanto, neste caso, tem o potencial de interferir na nossa capacidade de encontrar duplicatas. Por definição, a coluna de ID exclusivo garante que não haja duplicatas. Felizmente, podemos superar esse problema com bastante facilidade, como mostram os exemplos a seguir.

Opção 1


Provavelmente a maneira mais fácil/simples de fazer isso é com uma consulta simples que usa o GROUP BY cláusula:
SELECT 
    FirstName, 
    LastName, 
    COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName;

Resultado:
+-------------+------------+---------+
| FirstName   | LastName   | Count   |
|-------------+------------+---------|
| Wag         | Johnson    | 3       |
| Woof        | Jones      | 1       |
| Ruff        | Robinson   | 1       |
| Bark        | Smith      | 2       |
+-------------+------------+---------+

Conseguimos excluir a coluna de chave primária/ID exclusivo omitindo-a de nossa consulta.

O resultado nos diz que existem três linhas contendo Wag Johnson e duas linhas contendo Bark Smith. Estes são duplicados (ou triplicados no caso de Wag Johnson).

Opção 2


Podemos excluir não duplicados do resultado incluindo o HAVING cláusula em nossa consulta:
SELECT 
    FirstName, 
    LastName, 
    COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1;

Resultado:
+-------------+------------+---------+
| FirstName   | LastName   | Count   |
|-------------+------------+---------|
| Wag         | Johnson    | 3       |
| Bark        | Smith      | 2       |
+-------------+------------+---------+

Opção 3


Também podemos verificar duplicatas em colunas concatenadas. Por exemplo, podemos usar o CONCAT() função para concatenar nossas duas colunas:
SELECT
    DISTINCT CONCAT(FirstName, ' ', LastName) AS DogName,
    COUNT(*) AS Count
FROM Dogs
GROUP BY CONCAT(FirstName, ' ', LastName);

Resultado:
+---------------+---------+
| DogName       | Count   |
|---------------+---------|
| Bark Smith    | 2       |
| Ruff Robinson | 1       |
| Wag Johnson   | 3       |
| Woof Jones    | 1       |
+---------------+---------+

Opção 4


Podemos usar o ROW_NUMBER() função com a função PARTITION BY cláusula para criar 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:
SELECT 
    *,
    ROW_NUMBER() OVER ( 
        PARTITION BY FirstName, LastName 
        ORDER BY FirstName, LastName
        ) AS Row_Number
FROM Dogs;

Resultado:
+---------+-------------+------------+--------------+
| DogId   | FirstName   | LastName   | Row_Number   |
|---------+-------------+------------+--------------|
| 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            |
+---------+-------------+------------+--------------+

Um benefício desse método é que podemos ver cada linha duplicada, juntamente com sua coluna identificadora exclusiva, devido ao fato de não estarmos agrupando os resultados.

Opção 5


Também 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 Row_Number
        FROM Dogs
    )
SELECT * FROM cte WHERE Row_Number <> 1;

Resultado:
+---------+-------------+------------+--------------+
| DogId   | FirstName   | LastName   | Row_Number   |
|---------+-------------+------------+--------------|
| 2       | Bark        | Smith      | 2            |
| 6       | Wag         | Johnson    | 2            |
| 7       | Wag         | Johnson    | 3            |
+---------+-------------+------------+--------------+

Esta opção exclui não duplicados da saída.

Ele também exclui exatamente uma linha de cada duplicata da saída. Isso abre a porta para que possamos ativar o último SELECT * em um DELETE para desduplicar a tabela, mantendo uma de cada duplicata.

Opção 6


Aqui está uma maneira mais sucinta de obter a mesma saída do exemplo anterior:
SELECT * FROM Dogs 
WHERE DogId IN (
    SELECT DogId FROM Dogs 
    EXCEPT SELECT MIN(DogId) FROM Dogs 
    GROUP BY FirstName, LastName
    );

Resultado:
+-------+-----------+----------+
| DogId | FirstName | LastName |
+-------+-----------+----------+
|     2 | Bark      | Smith    |
|     6 | Wag       | Johnson  |
|     7 | Wag       | Johnson  |
+-------+-----------+----------+

Este exemplo não requer a geração de nosso próprio número de linha separado.

Opção 7


E, finalmente, aqui está uma técnica um pouco mais complicada para retornar linhas duplicadas:
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    |
+---------+-------------+------------+---------+-------------+------------+

Até o resultado parece mais complicado, mas ei, ele ainda nos mostra as duplicatas!