Um cenário comum no dia-a-dia, quando lidamos com bancos de dados, é a necessidade de identificarmos e removermos registros duplicados numa tabela. A dificuldade aumenta quando não há chave-primária nesta tabela.
A seguir vou demonstrar três métodos que podem ser utilizados para esta finalidade.
Criando a Tabela de Exemplo
Para ilustrar os métodos que vou apresentar, precisamos criar uma tabela de exemplo. Abaixo está o código para criarmos esta tabela.
if exists (select * from sysobjects where name like '%Celebridades%')
drop table Celebridades
go
create table Celebridades(
PrimeiroNome nvarchar(25),
Sobrenome nvarchar(25)
)
go
Quadro 1: Criando a tabela de exemplo
Uma vez definida a tabela, precisamos alimentá-la com os registros duplicados.
insert into Celebridades(PrimeiroNome,Sobrenome) values('Minka','Kelly')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Keeley','Hazell')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Stacy','Ferguson')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Minka','Kelly')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Keeley','Hazell')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Cheryl','Cole')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Jennifer','Garner')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Scarlett','Johanson')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Stacy','Ferguson')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Natalie','Portman')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Katheryn','Winnick')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Minka','Kelly')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Stacy','Ferguson')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Natalie','Portman')
insert into Celebridades(PrimeiroNome,Sobrenome) values('LeeLee','Sobieski')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Megan','Fox')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Minka','Kelly')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Natalie','Portman')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Arianny','Celeste')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Brittney','Palmer')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Natalie','Portman')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Keeley','Hazell')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Megan','Fox')
insert into Celebridades(PrimeiroNome,Sobrenome) values('Minka','Kelly')
Quadro 2: Alimentando a tabela de exemplo com registros duplicados
Você pode observar que em nossa tabela de exemplo alguns registros se repetem por duas, três e até quatro vezes. Agora vamos trabalhar na exclusão dos registros duplicados. Não podemos utilizar a declaração DELETE FROM para excluir os duplicados desta forma:
delete from Celebridades where PrimeiroNome = 'Minka' AND Sobrenome = 'Kelly'
Este comando excluiria todos os registros com o nome Minka Kelly, e este não é o resultado esperado. Acompanhe a seguir três alternativas diferentes para solucionar esta questão.
Método 1: Utilizando o SET ROWCOUNT
A sintaxe para a declaração SET ROWCOUNT é:
SET ROWCOUNT { numero | @var_numero }
SET ROWCOUNT limita o SQL Server a processar um número específico de registros. Desta forma, o processo é interrompido após atingir o número de registros informado no argumento. O valor padrão para ROWCOUNT é 0 (zero), o que não estabelece limite para o comando, retornando todos os registros que atendam a cláusula WHERE.
Depois de executar o comando ROWCOUNT para limitar os registros que serão retornados pelo SQL, você pode executar o mesmo comando informando 0 (zero) como parametro para desabilitar esta opção.
Vamos tomar como exemplo o nome 'Minka Kelly', 5 registros serão retornados.
select * from Celebridades where primeironome = 'Minka' and sobrenome = 'Kelly'
Neste caso, precisamos excluir 4 dos 5 registros repetidos. Para isto devemos utilizar o ROWCOUNT atribuindo valor 4 para o parametro requerido. Veja o comando a seguir:
set rowcount 4
delete from Celebridades where PrimeiroNome = N'Minka' and Sobrenome = N'Kelly'
set rowcount 0
-- (4 row(s) affected)
Depois de executar o comando acima, utilizando a declaração SET ROWCOUNT, execute o comando select e observe o resultado. Os registros repetidos foram excluídos.
select PrimeiroNome, Sobrenome from Celebridades
Você também pode utilizar o comando SET ROWCOUNT para excluir todos os registros duplicados da tabela de exemplo. Para atingirmos este objetivo, é necessário implementar um CURSOR.
Atenção ao declarar o CURSOR para identificar os registros duplicados, você deve definir o parametro do ROWCOUNT igual ao total de registros duplicados - 1, para preservar o registro original que será mantido na base após a execução deste comando.
Preste atenção no comando SET ROWCOUNT incluído no corpo do CURSOR.
declare @RegistrosDuplicados int
declare @PrimeiroNome nvarchar(25)
declare @Sobrenome nvarchar(25)
declare cursorDuplicados cursor fast_forward for
select PrimeiroNome, Sobrenome, count(*) - 1
from Celebridades
group by PrimeiroNome, Sobrenome
having count(*) > 1
open cursorDuplicados
fetch next from cursorDuplicados into @PrimeiroNome, @Sobrenome, @RegistrosDuplicados
while @@fetch_status = 0
begin
set rowcount @RegistrosDuplicados
delete from Celebridades where PrimeiroNome = @PrimeiroNome and Sobrenome = @Sobrenome
set rowcount 0
fetch next from cursorDuplicados into @PrimeiroNome, @Sobrenome, @RegistrosDuplicados
end
close cursorDuplicados
deallocate cursorDuplicados
Quadro 3: Utilizando o ROWCOUNT com CURSOR
Método 2: Utilizando TOP
Um segundo método que podemos usar para remover registros duplicados é a expressão TOP combinada com a declaração DELETE. Desde o SQL 2005, como uma evolução do T-SQL, a expressão TOP passou a aceitar uma variável onde antes, no SQL 2000, só era possível atribuir uma constante numérica. Com esta mudança, podemos utilizar a expressão TOP dentro de um CURSOR para excluir todos os registros duplicados de uma só vez.
Utilizando nossa tabela de exemplo, podemos construir um comando para excluir os registros duplicados com o nome 'Minka Kelly', como apresentado nas linhas a seguir.
delete top(4) from Celebridades
where PrimeiroNome = 'Minka' and Sobrenome = 'Kelly'
Trabalhando na mesma linha de raciocínio, podemos construir um CURSOR utilizando a expressão TOP. Veja o código abaixo.
declare @RegistrosDuplicados int
declare @PrimeiroNome nvarchar(25)
declare @Sobrenome nvarchar(25)
declare cursorDuplicados cursor fast_forward for
select PrimeiroNome, Sobrenome, count(*) - 1
from Celebridades
group by PrimeiroNome, Sobrenome
having count(*) > 1
open cursorDuplicados
fetch next from cursorDuplicados into @PrimeiroNome, @Sobrenome, @RegistrosDuplicados
while @@fetch_status = 0
begin
delete top(@RegistrosDuplicados) from Celebridades where PrimeiroNome = @PrimeiroNome and Sobrenome = @Sobrenome
fetch next from cursorDuplicados into @PrimeiroNome, @Sobrenome, @RegistrosDuplicados
end
close cursorDuplicados
deallocate cursorDuplicados
Quadro 4: Construindo CURSOR com a expressão TOP
Considere utilizar o método 2, com a expressão TOP, ao invés de utilizar o ROWCOUNT. A declaração ROWCOUNT não afetará os comandos DELETE, INSERT e UPDATE nas próximas versões do SQL. Consulte o Books On Line para mais informações (http://msdn.microsoft.com/en-us/library/ms188774.aspx).
Método 3: Adicionar uma coluna IDENTITY
O terceiro método é adicionar uma coluna IDENTITY na nossa tabela de exemplo. Execute o comando a seguir
para adicionar a coluna IDENTITY.
alter table Celebridades add Codigo int identity(1,1)
Agora, com a coluna IDENTITY podemos utilizar o comando DELETE para excluir os registros duplicados. Veja o exemplo a seguir.
delete from Celebridades where Codigo in(24,17,12,4)
Com a coluna IDENTITY inserida na nossa tabela de exemplo, podemos utilizar o recurso CTE (Common Table Expression) para excluir os registros duplicados, ao invés de usarmos um CURSOR. As CTEs foram introduzidas como aprimoramento do T-SQL a partir da versão 2005. Desta forma, o código abaixo não funcionará se voce tentar executá-lo no SQL Server 2000.
with CTEDuplicados(PrimeiroNome, Sobrenome, Codigo)
as
(
select PrimeiroNome, Sobrenome, Min(Codigo) Codigo
from Celebridades
group by PrimeiroNome, Sobrenome
having count(*) > 1
)
delete from Celebridades
where Codigo in (
select Celebridades.Codigo
from Celebridades
inner join CTEDuplicados
on Celebridades.PrimeiroNome = CTEDuplicados.PrimeiroNome
and Celebridades.Sobrenome = CTEDuplicados.Sobrenome
and Celebridades.Codigo <> CTEDuplicados.Codigo
)
Quadro 5: Utilizando CTE para excluir os registros duplicados
Após excluir os registros duplicados utilizando a CTE, você pode remover a coluna IDENTITY da tabela de exemplo executando o comando a seguir:
alter table Celebridades drop column Codigo
Aprofunde seus conhecimentos sobre este tema visitando a comunidade SQL Server no DevBrasil.
Faça download deste arquivo no formato PDF e também do código T-SQL deste exemplo.