PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

7 maneiras de encontrar linhas duplicadas no PostgreSQL enquanto ignora a chave primária


Aqui estão sete maneiras de retornar linhas duplicadas no PostgreSQL quando essas linhas têm uma chave primária ou outra coluna de identificador exclusivo.

Isso significa que as linhas duplicadas compartilham exatamente os mesmos valores em todas as colunas, com exceção da coluna de chave primária/ID exclusivo.

Dados de amostra


Usaremos os seguintes dados para nossos exemplos:
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 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 é uma boa prática em RDBMSs, pois as chaves primárias ajudam a reforçar a integridade dos dados. Mas como as chaves primárias evitam linhas duplicadas, elas têm o potencial de interferir em nossa capacidade de encontrar duplicatas.

Em nossa tabela acima, a coluna de chave primária é um número crescente e seu valor não tem significado e não é significativo. Portanto, precisamos ignorar essa linha se quisermos encontrar duplicatas nas outras colunas.

Opção 1


Podemos usar o SQL 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 
-----------+----------+-------
 Ruff      | Robinson |     1
 Wag       | Johnson  |     3
 Woof      | Jones    |     1
 Bark      | Smith    |     2

Aqui excluímos a coluna de chave primária 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). 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 
-----------+----------+-------
 Wag       | Johnson  |     3
 Bark      | Smith    |     2

Opção 3


Aqui está um exemplo de verificação de duplicatas em colunas concatenadas. Neste caso usamos 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 
---------------+-------
 Wag Johnson   |     3
 Ruff Robinson |     1
 Woof Jones    |     1
 Bark Smith    |     2

Opção 4


Alternativamente, podemos usar o ROW_NUMBER() função da janela:
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

Usando a PARTITION cláusula resulta na adição de uma nova coluna, com um número de linha que aumenta cada vez que há uma duplicata, mas é redefinido novamente quando há uma linha única.

Nesse caso, não agrupamos os resultados, o que significa que podemos ver cada linha duplicada, incluindo sua coluna de identificador exclusivo.

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

Isso exclui não duplicados da saída e exclui uma linha de cada duplicata da saída. Em outras palavras, ele mostra apenas as linhas em excesso das duplicatas. Essas linhas são as principais candidatas a serem excluídas em uma operação de desduplicação.

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 
    EXCEPT SELECT MIN(DogId) FROM Dogs 
    GROUP BY FirstName, LastName
    );

Resultado:
 dogid | firstname | lastname 
-------+-----------+----------
     6 | Wag       | Johnson
     2 | Bark      | Smith
     7 | Wag       | Johnson

Uma diferença entre este exemplo e o anterior é que este exemplo não requer a geração de nosso próprio número de linha separado.

Opção 7


Aqui está outra opção para retornar linhas duplicadas no Postgres:
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