Eliminando registros duplicados com um simples “DELETE”

Às vezes, nós DBA’s, recebemos a tarefa de corrigir erros de Análise/desenvolvimento. Este é um erro mais comum do que imaginam, como foi o caso hoje de um erro relatado por um usuário que estava vendo os registros duplicados em sua tabela de usuários do sistema. Esse erro não é de banco de dados!!!

O problema é que o Analista de Sistemas não definiu uma restrição de chave primária para impedir a duplicidade nas informações. Com isso, o DBA que provavelmente é a pessoa que mais entende de SQL na empresa (Isso não deveria ser assim e nem sempre é verdade), mas na maioria dos casos é a mais pura verdade, tem que entrar em ação para remover essa duplicidade com base na chave que o analista vai apontar para o DBA.

Vamos simular o problema aqui:

Criando uma tabela de usuários:

SQL> CREATE TABLE USUARIO

2 (CODIGO NUMBER, — deveria ser a chave pensada e não criada

3 NOME VARCHAR2(10),

4 CADASTRO DATE);

Tabela criada.

Depois do sistema (não validado) e posto em produção, os usuários fizeram algumas centenas de cargas e é claro que alguma coisa vai dar errado, pois a tabela não tem restrição de chave primária.
INSERT INTO USUARIO VALUES (1,’JOSE’, TO_DATE (’15/02/2010′, ‘DD/MM/YYYY’));
INSERT INTO USUARIO VALUES (1,’JOSE’, TO_DATE (’15/02/2010′, ‘DD/MM/YYYY’));
INSERT INTO USUARIO VALUES (1,’JOSE’, TO_DATE (’15/02/2010′, ‘DD/MM/YYYY’));
INSERT INTO USUARIO VALUES (2,’MARIA’ , TO_DATE (’16/02/2010′, ‘DD/MM/YYYY’));
INSERT INTO USUARIO VALUES (3,’ANA’ , TO_DATE (’16/02/2010′, ‘DD/MM/YYYY’));
INSERT INTO USUARIO VALUES (4,’CARMEN’, TO_DATE (’16/02/2010′, ‘DD/MM/YYYY’));

1 linha criada.

1 linha criada.

1 linha criada.

1 linha criada.

1 linha criada.

1 linha criada.

Carregamos algumas linhas para exemplo

SQL> COMMIT ;

Commit concluído.

Confirmei a transação de carga, agora os registros estão definitivamente armazenados no banco, contendo informações duplicadas. Podemos verificar o conteúdo da tabela USUARIO, com o select abaixo, e podemos constatar que os códigos de usuário estão duplicados por falta de chave primária na tabela.

SQL> SELECT *
2 FROM USUARIO;

CODIGO NOME CADASTRO
———- ———- ——————–
1 JOSE 15-FEV-2010 00:00:00
1 JOSE 15-FEV-2010 00:00:00
1 JOSE 15-FEV-2010 00:00:00
2 MARIA 16-FEV-2010 00:00:00
3 ANA 16-FEV-2010 00:00:00
4 CARMEN 16-FEV-2010 00:00:00
SQL> SELECT
2 CODIGO
3 , NOME
4 , COUNT(1)
5 FROM
6 USUARIO
7 GROUP BY
8 CODIGO
9 , NOME
10 ORDER BY
11 CODIGO;
CODIGO NOME COUNT(1)
———- ———- ———-
1 JOSE 3
2 MARIA 1
3 ANA 1
4 CARMEN 1

Então temos uma abundância de usuario JOSE no meu exemplo, e eu quero eliminar o somente as duplicatas que existam no meu banco. Isso é facil usando o ROWID em uma meia duzia de linhas, porem, essa tarefa se torna mais dificl quando essas linhas duplicadas chegam na casa de 1000 linhas ou mais. Imagina ficar catando ROWID por ROWID para deletar? Duvida? Eu já tive que fazer isso quando eu era DBA Jr. e não conhecia o poder do Google. Se você esta lendo esse post, terá bem menos trabalho do que eu tive um dia.

Como mágica, basta usar uma subquery, com isso, resolvemos facilmente o problema.
SQL> DELETE
2 FROM USUARIO
3 WHERE ROWID NOT IN (
4 SELECT MIN (ROWID)
5 FROM USUARIO
6 GROUP BY CODIGO
7 );

2 linhas deletadas.

Analisando o que o comando acima faz:

1) Pega os menores rowid’s agrupando isso pelas colunas de minha restricao de chave primaria;

2) Deleta todas as linhas que não contem esses rowid’s, logo ele so vai ficar com apenas uma linha de cada registro que eu agrupei.

SQL> COMMIT ;

Commit concluído.

Confirmei a informação no banco de dados, e agora vamos corrigir o problema inicial, que e criar a restrição de chave primária na tabela USUARIO do meu banco de dados.
SQL> ALTER TABLE USUARIO
2 ADD CONSTRAINT PK_USUARIO PRIMARY KEY (CODIGO);

Tabela alterada.

Agora, se eu tentar inserir um registro que tenha o mesmo valor de codigo na minha base o Oracle vai gritar logo.

SQL> INSERT INTO USUARIO VALUES (4,’JOSEFA’, TO_DATE (’16/02/2010′, ‘DD/MM/YYYY’));

SQL> INSERT INTO USUARIO VALUES (4,’JOSEFA’, TO_DATE (’16/02/2010′, ‘DD/MM/YYYY’));
INSERT INTO USUARIO VALUES (4,’JOSEFA’, TO_DATE (’16/02/2010′, ‘DD/MM/YYYY’))
*
ERRO na linha 1:
ORA-00001: unique constraint (MYUSER.PK_USUARIO) violated

Entao, regra basica, sempre criem uma chave primaria ou uma chave unica, isso esta e a primeira forma normal.

Dúvidas e comentários,

rodrigo (at) mufalani (dot) com (dot) br

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios são marcados com *