Os exemplos a seguir retornam linhas duplicadas de uma tabela do Oracle Database.
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
As duas primeiras linhas são duplicadas, assim como as três últimas linhas. Nesse caso, as linhas duplicadas contêm valores duplicados em todas as colunas, incluindo a coluna ID.
Opção 1
Podemos usar a seguinte consulta para ver quantas linhas são duplicadas:
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
Agrupamos as linhas por todas as colunas e retornamos a contagem de linhas de cada grupo. Qualquer linha com uma contagem maior que 1 é uma duplicata.
Podemos ordená-lo por contagem em ordem decrescente, para que as linhas com mais duplicatas apareçam primeiro:
SELECT
PetId,
PetName,
PetType,
COUNT(*) AS "Count"
FROM Pets
GROUP BY
PetId,
PetName,
PetType
ORDER BY Count(*) DESC;
Resultado:
PETID PETNAME PETTYPE Count 4 Bark Dog 3 1 Wag Dog 2 2 Scratch Cat 1 3 Tweet Bird 1
Opção 2
Se quisermos apenas as linhas duplicadas listadas, podemos usar o
HAVING
cláusula para retornar apenas linhas com uma contagem maior que 1:SELECT
PetId,
PetName,
PetType,
COUNT(*) AS "Count"
FROM Pets
GROUP BY
PetId,
PetName,
PetType
HAVING COUNT(*) > 1
ORDER BY COUNT(*) DESC;
Resultado:
PETID PETNAME PETTYPE Count 4 Bark Dog 3 1 Wag Dog 2
Opção 3
Outra opção é usar o
ROW_NUMBER()
função da janela:SELECT
PetId,
PetName,
PetType,
ROW_NUMBER() OVER (
PARTITION BY PetId, PetName, PetType
ORDER BY PetId, PetName, PetType
) AS rn
FROM Pets;
Resultado:
PETID PETNAME PETTYPE RN 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
Podemos usar a consulta acima como uma expressão de tabela comum:
WITH cte AS
(
SELECT
PetId,
PetName,
PetType,
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
Isso retorna apenas as linhas em excesso das duplicatas correspondentes. Portanto, se houver duas linhas idênticas, ele retornará uma delas. Se houver três linhas idênticas, ele retornará duas e assim por diante.
Opção 5
Dado que nossa tabela não contém uma coluna de chave primária, podemos aproveitar o
rowid
do Oracle pseudocoluna:SELECT * FROM Pets
WHERE EXISTS (
SELECT 1 FROM Pets p2
WHERE Pets.PetName = p2.PetName
AND Pets.PetType = p2.PetType
AND Pets.rowid > p2.rowid
);
Resultado:
PETID PETNAME PETTYPE 1 Wag Dog 4 Bark Dog 4 Bark Dog
A maneira como isso funciona é que cada linha em um banco de dados 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. No entanto, é importante observar que linhas em tabelas diferentes armazenadas juntas no mesmo cluster podem ter o mesmo rowid
. Um benefício do exemplo acima é que podemos substituir
SELECT *
com DELETE
para desduplicar a tabela. Opção 6
E finalmente, aqui está outra opção que usa o
rowid
pseudocoluna:SELECT * FROM Pets
WHERE rowid > (
SELECT MIN(rowid) FROM Pets p2
WHERE Pets.PetName = p2.PetName
AND Pets.PetType = p2.PetType
);
Resultado:
PETID PETNAME PETTYPE 1 Wag Dog 4 Bark Dog 4 Bark Dog
Mesmo resultado do exemplo anterior.
Assim como no exemplo anterior, podemos substituir
SELECT *
com DELETE
para remover linhas duplicadas da tabela.