Excluindo linhas duplicadas do banco de dados sqlite

91

Eu tenho uma tabela enorme - 36 milhões de linhas - no SQLite3. Nesta tabela muito grande, existem duas colunas:

  • hash - texto
  • d - real

Algumas das linhas são duplicadas. Ou seja, ambos hashe dtêm os mesmos valores. Se dois hashes forem idênticos, os valores de d. No entanto, dois valores idênticos dnão implicam em dois valores idênticos hash.

Quero excluir as linhas duplicadas. Não tenho uma coluna de chave primária.

Qual é a maneira mais rápida de fazer isso?

Patches
fonte
Coloque as respostas nos blocos de resposta. Mais tarde, você pode aceitar sua própria resposta. Veja também Como funciona a aceitação de uma resposta?
JWW

Respostas:

120

Você precisa encontrar uma maneira de distinguir as linhas. Com base em seu comentário, você pode usar a coluna especial rowid para isso.

Para excluir duplicatas, mantendo o menor rowidpor (hash,d):

delete   from YourTable
where    rowid not in
         (
         select  min(rowid)
         from    YourTable
         group by
                 hash
         ,       d
         )
Andomar
fonte
SQLite não permite adicionar uma coluna de chave primária, permite?
Patches de
sqlite> alter table dist add id integer primary key autoincrement; Error: Cannot add a PRIMARY KEY column
Patches de
Interessante! A parte de que você precisa é o autoincrementpensamento. Funciona se você omitir a primary keyparte?
Andomar
sqlite> alter table dist add id integer autoincrement; Error: near "autoincrement": syntax error Edit: SQLite tem um tipo de pseudo coluna "rowid" que está automaticamente lá, eu poderia usar isso?
Patches de
1
delete from dist where rowid not in (select max(rowid) from dist group by hash); Parece fazer o truque! Obrigado.
Patches de
5

Acho que o mais rápido seria usar o próprio banco de dados para isso: adicione uma nova tabela com as mesmas colunas, mas com as restrições adequadas (um índice exclusivo no par hash / real?), Itere através da tabela original e tente inserir registros a nova tabela, ignorando erros de violação de restrição (ou seja, continuar iterando quando as exceções forem levantadas).

Em seguida, exclua a tabela antiga e renomeie a nova para a antiga.

MaDa
fonte
Não é tão elegante quanto simplesmente alterar a tabela, eu acho, MAS uma coisa realmente boa sobre sua abordagem é que você pode executá-la novamente quantas vezes quiser, sem tocar / destruir os dados de origem até que esteja absolutamente satisfeito com os resultados .
Adrian K de
1

Se adicionar uma chave primária não for uma opção, uma abordagem seria armazenar as duplicatas DISTINCT em uma tabela temporária, excluir todos os registros duplicados da tabela existente e, em seguida, adicionar os registros de volta à tabela original da tabela temporária .

Por exemplo (escrito para SQL Server 2008, mas a técnica é a mesma para qualquer banco de dados):

DECLARE @original AS TABLE([hash] varchar(20), [d] float)
INSERT INTO @original VALUES('A', 1)
INSERT INTO @original VALUES('A', 2)
INSERT INTO @original VALUES('A', 1)
INSERT INTO @original VALUES('B', 1)
INSERT INTO @original VALUES('C', 1)
INSERT INTO @original VALUES('C', 1)

DECLARE @temp AS TABLE([hash] varchar(20), [d] float)
INSERT INTO @temp
SELECT [hash], [d] FROM @original 
GROUP BY [hash], [d]
HAVING COUNT(*) > 1

DELETE O
FROM @original O
JOIN @temp T ON T.[hash] = O.[hash] AND T.[d] = O.[d]

INSERT INTO @original
SELECT [hash], [d] FROM @temp

SELECT * FROM @original

Não tenho certeza se sqlite tem uma ROW_NUMBER()função de tipo, mas se tiver, você também pode tentar algumas das abordagens listadas aqui: Excluir registros duplicados de uma tabela SQL sem uma chave primária

rsbarro
fonte
+1, mas não tenho certeza se sqlite suporta a delete <alias> from <table> <alias>sintaxe
Andomar