Como encontrar duplicatas em 2 colunas, não em 1

107

Tenho uma tabela de banco de dados MySQL com duas colunas que me interessam. Individualmente, cada um deles pode ter duplicatas, mas nunca deve haver uma duplicata de AMBOS com o mesmo valor.

stone_idpode ter duplicatas, desde que cada upshargetítulo seja diferente e ao contrário. Mas digamos, por exemplo, stone_id= 412 e upcharge_title= "safira", essa combinação deve ocorrer apenas uma vez.

Isso está ok:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "ruby"

Isso NÃO está ok:

stone_id = 412 upcharge_title = "sapphire"
stone_id = 412 upcharge_title = "sapphire"

Existe uma consulta que localizará duplicatas em ambos os campos? E se possível, há uma maneira de definir meu banco de dados para não permitir isso?

Estou usando o MySQL versão 4.1.22

JD Isaacks
fonte

Respostas:

192

Você deve configurar uma chave composta entre os dois campos. Isso exigirá um stone_id e upcharge_title exclusivos para cada linha.

Para encontrar as duplicatas existentes, tente o seguinte:

select   stone_id,
         upcharge_title,
         count(*)
from     your_table
group by stone_id,
         upcharge_title
having   count(*) > 1
Miyagi Coder
fonte
Obrigado, isso os seleciona. Você poderia ter a gentileza de me dizer como deletar duplicatas (mas deixe 1 cópia, é claro) OBRIGADO!
JD Isaacks
2
Uma maneira seria pegar todos os dados distintos e recriar a tabela.
Miyagi Coder
1
@John Isaacks: Se não houver outros campos com os quais você possa distingui-los (ou seja, todos os campos são duplicados), você terá que deletar ambas as linhas e recriar uma. Uma maneira seria copiar duplicatas em uma cópia da tabela, excluí-las do original e reinserir linhas distintas da cópia.
P Daddy
Isso não funciona no postgres 8.1, alguém poderia me ajudar nisso?
Lennon
muito obrigado, a ordem que você agrupa por assunto?
Andrew
35

Achei útil adicionar um índice exclusivo usando um "ALTER IGNORE" que remove as duplicatas e impõe registros exclusivos, o que parece que você gostaria de fazer. Portanto, a sintaxe seria:

ALTER IGNORE TABLE `table` ADD UNIQUE INDEX(`id`, `another_id`, `one_more_id`);

Isso efetivamente adiciona a restrição exclusiva, o que significa que você nunca terá registros duplicados e o IGNORE exclui as duplicatas existentes.

Você pode ler mais sobre eh ALTER IGNORE aqui: http://mediakey.dk/~cc/mysql-remove-duplicate-entries/

Update: Fui informado por @Inquisitive que isso pode falhar nas versões do MySql> 5.5:

Ele falha no MySQL> 5.5 e na tabela InnoDB, e no Percona por causa de seu recurso de criação rápida de índice InnoDB [ http://bugs.mysql.com/bug.php?id=40344] . Neste caso, execute primeiro set session old_alter_table=1e então o comando acima funcionará bem

Atualização - ALTER IGNORERemovido em 5.7

Dos documentos

A partir do MySQL 5.6.17, a cláusula IGNORE está obsoleta e seu uso gera um aviso. IGNORE foi removido do MySQL 5.7.

Um dos desenvolvedores do MySQL oferece duas alternativas :

  • Agrupe pelos campos exclusivos e exclua como visto acima
  • Crie uma nova tabela, adicione um índice exclusivo, use INSERT IGNORE, por exemplo:
CREATE TABLE duplicate_row_table LIKE regular_row_table;
ALTER TABLE duplicate_row_table ADD UNIQUE INDEX (id, another_id);
INSERT IGNORE INTO duplicate_row_table SELECT * FROM regular_row_table;
DROP TABLE regular_row_table;
RENAME TABLE duplicate_row_table TO regular_row_table;

Mas dependendo do tamanho da sua mesa, isso pode não ser prático

SeanDowney
fonte
1
Verdade, mas pelo menos na próxima vez que você souber. Tive o mesmo problema e achei bom compartilhar com outras pessoas
SeanDowney
Eu só estava brincando sobre o atraso de 3 anos. Realmente estou feliz que você compartilhou. Daí o mais 1.
JD Isaacks
Imagino que isso remova uma das duplicatas arbitrariamente, portanto, certifique-se de que não haja dados diferentes entre cada linha que possam ser úteis para saber ou manter.
Joshua Pinter
1 para a resposta mesmo após 2 anos de atraso. Eu acidentalmente apaguei uma chave composta e isso salvou minha vida. Obrigado
ivcode
Eu tentei algumas técnicas de localizador de duplicação e nenhuma delas foi tão simples e rápida. Obrigado por compartilhar este método.
Kristjan O.
8

Você pode encontrar duplicatas como esta ..

Select
    stone_id, upcharge_title, count(*)
from 
    particulartable
group by 
    stone_id, upcharge_title
having 
    count(*) > 1
Jason Punyon
fonte
4

Para encontrar as duplicatas:

select stone_id, upcharge_title from tablename group by stone_id, upcharge_title having count(*)>1

Para evitar isso no futuro, crie uma chave única composta nesses dois campos.

Ian Nelson
fonte
1
Muito obrigado, você pode me dizer como excluir todas, exceto uma das duplicatas. E como faço para configurar uma chave compisite no phpmyadmin. OBRIGADO!!!
JD Isaacks
3

A propósito, uma restrição única composta na tabela evitaria que isso ocorresse em primeiro lugar.

ALTER TABLE table
    ADD UNIQUE(stone_id, charge_title)

(Este é um T-SQL válido. Não tenho certeza sobre o MySQL.)

P papai
fonte
1
Acho que funciona, mas não me deixará fazer isso até que eu remova as duplicatas primeiro. Obrigado.
JD Isaacks
1

este post do SO me ajudou, mas eu também queria saber como excluir e manter uma das linhas ... aqui está uma solução PHP para excluir as linhas duplicadas e manter uma (no meu caso, havia apenas 2 colunas e está em um função para limpar associações de categoria duplicadas)

$dupes = $db->query('select *, count(*) as NUM_DUPES from PRODUCT_CATEGORY_PRODUCT group by fkPRODUCT_CATEGORY_ID, fkPRODUCT_ID having count(*) > 1');
if (!is_array($dupes))
    return true;
foreach ($dupes as $dupe) {
    $db->query('delete from PRODUCT_CATEGORY_PRODUCT where fkPRODUCT_ID = ' . $dupe['fkPRODUCT_ID'] . ' and fkPRODUCT_CATEGORY_ID = ' . $dupe['fkPRODUCT_CATEGORY_ID'] . ' limit ' . ($dupe['NUM_DUPES'] - 1);
}

o (limite NUM_DUPES - 1) é o que preserva a única linha ...

obrigado a todos

groovenectar
fonte
3
ALTER IGNORE TABLE table ADD UNIQUE INDEX index_name(stone_id, charge_title)irá remover linhas duplicadas, deixando apenas um par exclusivo.
dev-null-morador de