Eu preciso de DELETE
linhas duplicadas para sid especificado em uma MySQL
tabela.
Como posso fazer isso com uma consulta SQL?
DELETE (DUPLICATED TITLES) FROM table WHERE SID = "1"
Algo assim, mas não sei como fazê-lo.
Eu preciso de DELETE
linhas duplicadas para sid especificado em uma MySQL
tabela.
Como posso fazer isso com uma consulta SQL?
DELETE (DUPLICATED TITLES) FROM table WHERE SID = "1"
Algo assim, mas não sei como fazê-lo.
Respostas:
isso remove duplicatas no local, sem criar uma nova tabela
ALTER IGNORE TABLE `table_name` ADD UNIQUE (title, SID)
nota: só funciona bem se o índice couber na memória
ALTER IGNORE
.
ALTER TABLE foo ENGINE MyISAM
para contornar isso, troquei o motor depois.
Suponha que você tenha uma tabela employee
com as seguintes colunas:
employee (first_name, last_name, start_date)
Para excluir as linhas com uma first_name
coluna duplicada :
delete
from employee using employee,
employee e1
where employee.id > e1.id
and employee.first_name = e1.first_name
employee
se a si próprio para uma correspondência de índice e uma >
verificação em um índice será lenta para tabelas grandes. Não seria melhor SELECT MAX(ID) FROM t GROUP BY unique
e então JOIN
uma correspondência exata de ID
para MAX(ID)
?
A seguir, remova duplicatas para todos os SID-s, não apenas um.
Com tabela temporária
CREATE TABLE table_temp AS
SELECT * FROM table GROUP BY title, SID;
DROP TABLE table;
RENAME TABLE table_temp TO table;
Desde que temp_table
foi criado recentemente, não possui índices. Você precisará recriá-los após remover as duplicatas. Você pode verificar quais índices você possui na tabela comSHOW INDEXES IN table
Sem tabela temporária:
DELETE FROM `table` WHERE id IN (
SELECT all_duplicates.id FROM (
SELECT id FROM `table` WHERE (`title`, `SID`) IN (
SELECT `title`, `SID` FROM `table` GROUP BY `title`, `SID` having count(*) > 1
)
) AS all_duplicates
LEFT JOIN (
SELECT id FROM `table` GROUP BY `title`, `SID` having count(*) > 1
) AS grouped_duplicates
ON all_duplicates.id = grouped_duplicates.id
WHERE grouped_duplicates.id IS NULL
)
SELECT * FROM table GROUP BY title, SID;
Tudo depende de quão bem você sabe o que está fazendo.
Crie a tabela e insira algumas linhas:
create table penguins(foo int, bar varchar(15), baz datetime);
insert into penguins values(1, 'skipper', now());
insert into penguins values(1, 'skipper', now());
insert into penguins values(3, 'kowalski', now());
insert into penguins values(3, 'kowalski', now());
insert into penguins values(3, 'kowalski', now());
insert into penguins values(4, 'rico', now());
select * from penguins;
+------+----------+---------------------+
| foo | bar | baz |
+------+----------+---------------------+
| 1 | skipper | 2014-08-25 14:21:54 |
| 1 | skipper | 2014-08-25 14:21:59 |
| 3 | kowalski | 2014-08-25 14:22:09 |
| 3 | kowalski | 2014-08-25 14:22:13 |
| 3 | kowalski | 2014-08-25 14:22:15 |
| 4 | rico | 2014-08-25 14:22:22 |
+------+----------+---------------------+
6 rows in set (0.00 sec)
Remova as duplicatas no lugar:
delete a
from penguins a
left join(
select max(baz) maxtimestamp, foo, bar
from penguins
group by foo, bar) b
on a.baz = maxtimestamp and
a.foo = b.foo and
a.bar = b.bar
where b.maxtimestamp IS NULL;
Query OK, 3 rows affected (0.01 sec)
select * from penguins;
+------+----------+---------------------+
| foo | bar | baz |
+------+----------+---------------------+
| 1 | skipper | 2014-08-25 14:21:59 |
| 3 | kowalski | 2014-08-25 14:22:15 |
| 4 | rico | 2014-08-25 14:22:22 |
+------+----------+---------------------+
3 rows in set (0.00 sec)
Você terminou, as linhas duplicadas são removidas, a última por carimbo de data / hora é mantida.
Você não tem uma timestamp
ou uma coluna de índice exclusiva para classificar? Você está vivendo em um estado de degeneração. Você precisará executar etapas adicionais para excluir linhas duplicadas.
crie a tabela de pinguins e adicione algumas linhas
create table penguins(foo int, bar varchar(15));
insert into penguins values(1, 'skipper');
insert into penguins values(1, 'skipper');
insert into penguins values(3, 'kowalski');
insert into penguins values(3, 'kowalski');
insert into penguins values(3, 'kowalski');
insert into penguins values(4, 'rico');
select * from penguins;
# +------+----------+
# | foo | bar |
# +------+----------+
# | 1 | skipper |
# | 1 | skipper |
# | 3 | kowalski |
# | 3 | kowalski |
# | 3 | kowalski |
# | 4 | rico |
# +------+----------+
faça um clone da primeira tabela e copie para ela.
drop table if exists penguins_copy;
create table penguins_copy as ( SELECT foo, bar FROM penguins );
#add an autoincrementing primary key:
ALTER TABLE penguins_copy ADD moo int AUTO_INCREMENT PRIMARY KEY first;
select * from penguins_copy;
# +-----+------+----------+
# | moo | foo | bar |
# +-----+------+----------+
# | 1 | 1 | skipper |
# | 2 | 1 | skipper |
# | 3 | 3 | kowalski |
# | 4 | 3 | kowalski |
# | 5 | 3 | kowalski |
# | 6 | 4 | rico |
# +-----+------+----------+
O agregado máximo opera com o novo índice moo:
delete a from penguins_copy a left join(
select max(moo) myindex, foo, bar
from penguins_copy
group by foo, bar) b
on a.moo = b.myindex and
a.foo = b.foo and
a.bar = b.bar
where b.myindex IS NULL;
#drop the extra column on the copied table
alter table penguins_copy drop moo;
select * from penguins_copy;
#drop the first table and put the copy table back:
drop table penguins;
create table penguins select * from penguins_copy;
observar e limpar
drop table penguins_copy;
select * from penguins;
+------+----------+
| foo | bar |
+------+----------+
| 1 | skipper |
| 3 | kowalski |
| 4 | rico |
+------+----------+
Elapsed: 1458.359 milliseconds
O que essa grande instrução de exclusão SQL está fazendo?
Os pinguins de mesa com o pseudônimo 'a' são deixados unidos em um subconjunto de pinguins de mesa chamado pseudônimo 'b'. A tabela à direita 'b', que é um subconjunto, encontra o timestamp máximo [ou max moo] agrupado pelas colunas foo e bar. Isso corresponde à tabela da esquerda 'a'. (foo, bar, baz) à esquerda tem todas as linhas da tabela. O subconjunto direito 'b' possui um (carimbo de data e hora máx, foo, barra) que corresponde à esquerda apenas no que é o valor máx.
Cada linha que não seja esse max possui o valor maxtimestamp de NULL. Filtre essas linhas NULL e você terá um conjunto de todas as linhas agrupadas por foo e bar que não é o mais recente registro de data e hora. Exclua aqueles.
Faça um backup da tabela antes de executar isso.
Evite que esse problema aconteça novamente nesta tabela:
Se você conseguiu que isso funcionasse, e apagou o fogo da "linha duplicada". Ótimo. Agora defina uma nova chave exclusiva composta em sua tabela (nessas duas colunas) para impedir que mais duplicatas sejam adicionadas em primeiro lugar.
Como um bom sistema imunológico, as linhas defeituosas nem deveriam ser permitidas na tabela no momento da inserção. Posteriormente, todos os programas que adicionarem duplicados transmitirão seu protesto e, quando você os corrigir, esse problema nunca será exibido novamente.
ID
coluna de incremento automático , a ON
cláusula precisará apenas corresponder à ID
coluna, nada mais.
Depois de me deparar com esse problema, em um grande banco de dados, não fiquei completamente impressionado com o desempenho de nenhuma das outras respostas. Quero manter apenas a última linha duplicada e excluir o restante.
Em uma declaração de uma consulta, sem uma tabela temporária, isso funcionou melhor para mim,
DELETE e.*
FROM employee e
WHERE id IN
(SELECT id
FROM (SELECT MIN(id) as id
FROM employee e2
GROUP BY first_name, last_name
HAVING COUNT(*) > 1) x);
A única ressalva é que eu tenho que executar a consulta várias vezes, mas mesmo assim, achei que funcionou melhor para mim do que as outras opções.
Isso sempre parece funcionar para mim:
CREATE TABLE NoDupeTable LIKE DupeTable;
INSERT NoDupeTable SELECT * FROM DupeTable group by CommonField1,CommonFieldN;
Que mantém o ID mais baixo em cada um dos enganos e no restante dos registros não fraudulentos.
Também fiz o seguinte para que o problema do dupe não ocorra mais após a remoção:
CREATE TABLE NoDupeTable LIKE DupeTable;
Alter table NoDupeTable Add Unique `Unique` (CommonField1,CommonField2);
INSERT IGNORE NoDupeTable SELECT * FROM DupeTable;
Em outras palavras, eu crio uma duplicata da primeira tabela, adiciono um índice exclusivo nos campos dos quais não quero duplicatas e, em seguida, faço um Insert IGNORE
que tenha a vantagem de não falhar normalmente Insert
, na primeira vez que tentasse adicionar um registro duplicado com base nos dois campos e ignora esses registros.
Movendo para a frente, torna-se impossível criar registros duplicados com base nesses dois campos.
ORDER BY
no SELECT
para ter a certeza que registram realmente torna para o NoDupeTable
?
ORDER by ID Asc
não poderia machucar, então vou editar minha resposta.
Select Max(ID)
e então, Order by Max(ID)
mas tudo o que faria é inverter a ordem da inserção. Para obter o ID mais alto, seria necessária uma seleção mais complexa, pois, independentemente de como você solicitou acima, você estará obtendo os valores de campo do ID mais baixo.
MAX(ID)
ou ou MIN(ID)
nomes de colunas em vez de *
no SELECT FROM DupeTable
entanto, caso contrário, você apenas obterá um dos ID
aleatoriamente. De fato, muitos SQLs e até mesmo o MySQL estrito requerem chamar uma função agregada em cada coluna não especificada na GROUP BY
cláusula.
ID,First,Last,Notes
e registros 1,Bob,Smith,NULL
e 2,Bob,Smith,Arrears
, ao fazer um SELECT *Max(ID), First,Last,Notes FROM DupeTable group by First,Last
, ambos retornariam o mesmo registro, 1, exceto com um ID diferente. Max (ID) retornaria 2,Bob,Smith,NULL
e Min (ID) retornaria 1,Bob,Smith,NULL
. Para obter o segundo registro com `Atrasados 'nas notas, é necessário juntar-me.
Aqui está uma resposta simples:
delete a from target_table a left JOIN (select max(id_field) as id, field_being_repeated
from target_table GROUP BY field_being_repeated) b
on a.field_being_repeated = b.field_being_repeated
and a.id_field = b.id_field
where b.id_field is null;
and a.id_field = b.id
LEFT JOIN
to b
só precisa comparar b.id
= a.id_field
assumindo que field_id
é um ID de incremento automático exclusivo. assim a.field_being_repeated = b.field_being_repeated
é estranho. (também b.id_field
não existe nesta consulta b.id
.
Este trabalho para remover os registros antigos:
delete from table where id in
(select min(e.id)
from (select * from table) e
group by column1, column2
having count(*) > 1
);
Você pode substituir min (e.id) por max (e.id) para remover os registros mais recentes.
Considero a solução de Werner acima a mais conveniente porque funciona independentemente da presença de uma chave primária, não mexe com tabelas, usa sql simples à prova de futuro, é muito compreensível.
Como afirmei no meu comentário, essa solução ainda não foi devidamente explicada. Então isso é meu, com base nisso.
1) adicione uma nova coluna booleana
alter table mytable add tokeep boolean;
2) adicione uma restrição nas colunas duplicadas E na nova coluna
alter table mytable add constraint preventdupe unique (mycol1, mycol2, tokeep);
3) defina a coluna booleana como true. Isso terá êxito apenas em uma das linhas duplicadas devido à nova restrição
update ignore mytable set tokeep = true;
4) excluir linhas que não foram marcadas como manutenção
delete from mytable where tokeep is null;
5) solte a coluna adicionada
alter table mytable drop tokeep;
Sugiro que você mantenha a restrição adicionada, para evitar novas duplicatas no futuro.
Este procedimento removerá todas as duplicatas (incluindo múltiplos) em uma tabela, mantendo a última duplicada. Esta é uma extensão de Recuperando o último registro em cada grupo
Espero que isso seja útil para alguém.
DROP TABLE IF EXISTS UniqueIDs;
CREATE Temporary table UniqueIDs (id Int(11));
INSERT INTO UniqueIDs
(SELECT T1.ID FROM Table T1 LEFT JOIN Table T2 ON
(T1.Field1 = T2.Field1 AND T1.Field2 = T2.Field2 #Comparison Fields
AND T1.ID < T2.ID)
WHERE T2.ID IS NULL);
DELETE FROM Table WHERE id NOT IN (SELECT ID FROM UniqueIDs);
Outra maneira fácil ... usando UPDATE IGNORE:
Você tem que usar um índice em uma ou mais colunas (tipo índice). Crie uma nova coluna de referência temporária (não faça parte do índice). Nesta coluna, você marca os únicos, atualizando-o com a cláusula ignore. Passo a passo:
Adicione uma coluna de referência temporária para marcar os únicos:
ALTER TABLE `yourtable` ADD `unique` VARCHAR(3) NOT NULL AFTER `lastcolname`;
=> isso adicionará uma coluna à sua tabela.
Atualize a tabela, tente marcar tudo como único, mas ignore os possíveis erros devido a um problema de chave duplicado (os registros serão ignorados):
UPDATE IGNORE `yourtable` SET `unique` = 'Yes' WHERE 1;
=> você encontrará que seus registros duplicados não serão marcados como únicos = 'Sim'; em outras palavras, apenas um de cada conjunto de registros duplicados será marcado como exclusivo.
Exclua tudo o que não é exclusivo:
DELETE * FROM `yourtable` WHERE `unique` <> 'Yes';
=> Isso removerá todos os registros duplicados.
Solte a coluna ...
ALTER TABLE `yourtable` DROP `unique`;
unique
coluna DEVE ser adicionada a uma restrição exclusiva, juntamente com as colunas atualmente duplicadas; caso contrário, a coisa toda não funciona porque o SET unique
= 'Yes' nunca falhará.
unique
é uma palavra-chave mysql. Portanto, ele precisa ter os backticks (como já exibidos corretamente). Usar outra palavra para a coluna pode ser mais conveniente.
Excluir duplicatas em tabelas MySQL é um problema comum, que geralmente vem com necessidades específicas. Caso alguém esteja interessado, aqui ( Remover linhas duplicadas no MySQL ), explico como usar uma tabela temporária para excluir duplicatas do MySQL de maneira confiável e rápida, também válida para lidar com fontes de big data (com exemplos para diferentes casos de uso).
Ali , no seu caso, você pode executar algo como isto:
-- create a new temporary table
CREATE TABLE tmp_table1 LIKE table1;
-- add a unique constraint
ALTER TABLE tmp_table1 ADD UNIQUE(sid, title);
-- scan over the table to insert entries
INSERT IGNORE INTO tmp_table1 SELECT * FROM table1 ORDER BY sid;
-- rename tables
RENAME TABLE table1 TO backup_table1, tmp_table1 TO table1;
delete from `table` where `table`.`SID` in
(
select t.SID from table t join table t1 on t.title = t1.title where t.SID > t1.SID
)
A resposta de Love @ eric, mas não parece funcionar se você tiver uma mesa muito grande (estou recebendo The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET MAX_JOIN_SIZE=# if the SELECT is okay
quando tento executá-la). Portanto, limitei a consulta de junção para considerar apenas as linhas duplicadas e acabei com:
DELETE a FROM penguins a
LEFT JOIN (SELECT COUNT(baz) AS num, MIN(baz) AS keepBaz, foo
FROM penguins
GROUP BY deviceId HAVING num > 1) b
ON a.baz != b.keepBaz
AND a.foo = b.foo
WHERE b.foo IS NOT NULL
A cláusula WHERE nesse caso permite que o MySQL ignore qualquer linha que não tenha uma duplicata e também ignorará se esta for a primeira instância da duplicata, portanto, apenas as duplicatas subsequentes serão ignoradas. Mude MIN(baz)
para MAX(baz)
para manter a última instância em vez da primeira.
Isso funciona para tabelas grandes:
CREATE Temporary table duplicates AS select max(id) as id, url from links group by url having count(*) > 1;
DELETE l from links l inner join duplicates ld on ld.id = l.id WHERE ld.id IS NOT NULL;
Para excluir a alteração mais antiga max(id)
paramin(id)
Isso aqui transformará a coluna column_name
em uma chave primária e, enquanto isso, ignorará todos os erros. Portanto, ele excluirá as linhas com um valor duplicado para column_name
.
ALTER IGNORE TABLE `table_name` ADD PRIMARY KEY (`column_name`);
Eu acho que isso funcionará basicamente copiando a tabela e esvaziando-a e colocando apenas os valores distintos novamente nela, mas verifique-a novamente antes de fazê-lo em grandes quantidades de dados.
Cria uma cópia carbono da sua tabela
crie a tabela temp_table como oldtablename; insira temp_table selecione * do nome da tabela antiga;
Esvazia sua tabela original
DELETE * do nome da tabela antiga;
Copia todos os valores distintos da tabela copiada de volta para a tabela original
INSERT oldtablename SELECT * do grupo temp_table por nome, sobrenome, dob
Exclui sua tabela temporária.
Soltar tabela temp_table
Você precisa agrupar por todos os campos que deseja manter distintos.
DELETE T2
FROM table_name T1
JOIN same_table_name T2 ON (T1.title = T2.title AND T1.ID <> T2.ID)
aqui está como eu costumo eliminar duplicatas
Você pode simplesmente usar uma cláusula DISTINCT para selecionar a lista "limpa" (e aqui está um exemplo muito fácil de como fazer isso).
DISTINCT
você perde qualquer informação sobre duplicatas que possa ter recebido em primeiro lugar. Você pode mostrar uma maneira de excluir duplicatas usando-o?
Poderia funcionar se você contá-los e adicionar um limite à sua consulta de exclusão deixando apenas um?
Por exemplo, se você tiver dois ou mais, escreva sua consulta assim:
DELETE FROM table WHERE SID = 1 LIMIT 1;
Existem apenas algumas etapas básicas ao remover dados duplicados da sua tabela:
Aqui está o tutorial completo: https://blog.teamsql.io/deleting-duplicate-data-3541485b3473