Aqui estão onze opções para retornar linhas duplicadas no Oracle Database quando essas linhas têm uma chave primária ou alguma outra coluna de identificador exclusivo e você deseja ignorá-la.
Dados de amostra
Usaremos os seguintes dados para nossos exemplos:
SELECT * FROM Dogs;
Resultado:
DOGID | PRIMEIRO NOME | SOBRENOME |
---|---|---|
1 | Latido | Smith |
2 | Latido | Smith |
3 | Au | Jones |
4 | Ruff | Robinson |
5 | Agite | Johnson |
6 | Agite | Johnson |
7 | Agite | Johnson |
As duas primeiras linhas são duplicadas e as três últimas linhas são duplicadas. As linhas duplicadas compartilham exatamente os mesmos valores em todas as colunas, com exceção da coluna de chave primária/ID exclusivo.
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 o fato de as chaves primárias conterem valores únicos significa que precisamos ignorar essa coluna ao procurar por 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. Podemos, portanto, ignorar os dados dessa coluna ao procurar duplicatas.
Opção 1
Aqui está nossa primeira opção para retornar duplicatas:
SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
ORDER BY Count DESC;
Resultado:
FIRSTNAME | SOBRENOME | COUNT |
---|---|---|
Agite | Johnson | 3 |
Latido | Smith | 2 |
Ruff | Robinson | 1 |
Uau | Jones | 1 |
Aqui construímos nossa consulta com o
GROUP BY
cláusula para que a saída seja agrupada pelas colunas relevantes. Também usamos o COUNT()
função para retornar o número de linhas idênticas. E ordenamos por contagem em ordem decrescente para que as duplicatas apareçam primeiro. 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 adicionar o
HAVING
cláusula ao nosso exemplo anterior para excluir não duplicados da saída:SELECT
FirstName,
LastName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName, LastName
HAVING COUNT(*) > 1
ORDER BY Count DESC;
Resultado:
FIRSTNAME | SOBRENOME | COUNT |
---|---|---|
Agite | Johnson | 3 |
Latido | Smith | 2 |
Opção 3
Também podemos verificar duplicatas em colunas concatenadas. Neste caso, usamos o
DISTINCT
palavra-chave para obter valores distintos, então use o COUNT()
função para retornar a contagem:SELECT
DISTINCT FirstName || ' ' || LastName AS DogName,
COUNT(*) AS Count
FROM Dogs
GROUP BY FirstName || ' ' || LastName
ORDER BY Count DESC;
Resultado:
NOME DO CÃO | COUNT |
---|---|
Wag Johnson | 3 |
Bark Smith | 2 |
Ruff Robinson | 1 |
Woof Jones | 1 |
Opção 4
Cada linha no Oracle tem um
rowid
pseudocoluna que retorna o endereço da linha. O rowid
é um identificador exclusivo para linhas na tabela e geralmente seu valor identifica exclusivamente uma linha no banco de dados (embora seja importante observar que linhas em tabelas diferentes armazenadas juntas no mesmo cluster podem ter o mesmo rowid ).
De qualquer forma, podemos construir uma consulta que usa o rowid
se nós quisermos:
SELECT * FROM Dogs
WHERE EXISTS (
SELECT 1 FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
AND Dogs.rowid > d2.rowid
);
Resultado:
DOGID PRIMEIRO NOME SOBRENOME 2 Latido Smith 6 Agite Johnson 7 Agite Johnson
Poderíamos substituir o SELECT *
com EXCLUIR
para realizar uma operação de desduplicação na mesa.
Observe que poderíamos ter usado o DogId
coluna (nossa chave primária) em vez do rowid
se quiséssemos. Dito isso, o rowid
pode ser útil se você não puder usar a coluna de chave primária por algum motivo ou se a tabela não tiver uma chave primária.
Opção 5
Aqui está outra consulta que usa o rowid
:
SELECT * FROM Dogs
WHERE rowid > (
SELECT MIN(rowid) FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
);
Resultado:
DOGID PRIMEIRO NOME SOBRENOME 2 Latido Smith 6 Agite Johnson 7 Agite Johnson
Assim como no exemplo anterior, poderíamos substituir o SELECT *
com EXCLUIR
para excluir as linhas duplicadas.
Opção 6
Os dois rowid
as opções acima são ótimas se você deve ignorar completamente a chave primária em sua consulta (ou se você não tiver uma coluna de chave primária). No entanto, como mencionado, ainda há a opção de substituir rowid
com a coluna de chave primária – no nosso caso o DogId
coluna:
SELECT * FROM Dogs
WHERE EXISTS (
SELECT 1 FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
AND Dogs.DogId > d2.DogId
);
Resultado:
DOGID PRIMEIRO NOME SOBRENOME 2 Latido Smith 6 Agite Johnson 7 Agite Johnson
Opção 7
E aqui está a outra consulta com o rowid
substituído pelo DogId
coluna:
SELECT * FROM Dogs
WHERE DogId > (
SELECT MIN(DogId) FROM Dogs d2
WHERE Dogs.FirstName = d2.FirstName
AND Dogs.LastName = d2.LastName
);
Resultado:
DOGID PRIMEIRO NOME SOBRENOME 2 Latido Smith 6 Agite Johnson 7 Agite Johnson
Opção 8
Outra maneira de encontrar duplicatas é usar o ROW_NUMBER()
função da janela:
SELECT
DogId,
FirstName,
LastName,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS row_num
FROM Dogs;
Resultado:
DOGID PRIMEIRO NOME SOBRENOME ROW_NUM 1 Latido Smith 1 2 Latido Smith 2 4 Ruff Robinson 1 7 Agite Johnson 1 5 Agite Johnson 2 6 Agite Johnson 3 3 Au Jones 1
Usando a PARTIÇÃO
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 9
Também podemos usar o exemplo anterior como uma expressão de tabela comum em uma consulta maior:
WITH cte AS
(
SELECT
DogId,
FirstName,
LastName,
ROW_NUMBER() OVER (
PARTITION BY FirstName, LastName
ORDER BY FirstName, LastName
) AS row_num
FROM Dogs
)
SELECT * FROM cte WHERE row_num <> 1;
Resultado:
DOGID PRIMEIRO NOME SOBRENOME ROW_NUM 2 Latido Smith 2 5 Agite Johnson 2 6 Agite Johnson 3
Essa consulta exclui não duplicados da saída e exclui uma linha de cada duplicata da saída.
Opção 10
Aqui está outra maneira de obter a mesma saída do exemplo anterior:
SELECT * FROM Dogs
WHERE DogId IN (
SELECT DogId FROM Dogs
MINUS SELECT MIN(DogId) FROM Dogs
GROUP BY FirstName, LastName
);
Resultado:
DOGID PRIMEIRO NOME SOBRENOME 2 Latido Smith 6 Agite Johnson 7 Agite Johnson
Este exemplo usa o MINUS
do Oracle operador, que retorna apenas linhas exclusivas retornadas pela primeira consulta, mas não pela segunda.
O MENOS
operador é semelhante ao EXCEPT
em outros SGBDs, como SQL Server, MariaDB, PostgreSQL e SQLite.
Opção 11
Aqui está outra opção para selecionar duplicatas da nossa tabela:
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 PRIMEIRO NOME SOBRENOME CÃO PRIMEIRO NOME SOBRENOME 2 Latido Smith 1 Latido Smith 7 Agite Johnson 5 Agite Johnson 7 Agite Johnson 6 Agite Johnson