Potrzebuję DELETE
zduplikowanych wierszy dla określonego identyfikatora sid w MySQL
tabeli.
Jak mogę to zrobić za pomocą zapytania SQL?
DELETE (DUPLICATED TITLES) FROM table WHERE SID = "1"
Coś takiego, ale nie wiem, jak to zrobić.
Potrzebuję DELETE
zduplikowanych wierszy dla określonego identyfikatora sid w MySQL
tabeli.
Jak mogę to zrobić za pomocą zapytania SQL?
DELETE (DUPLICATED TITLES) FROM table WHERE SID = "1"
Coś takiego, ale nie wiem, jak to zrobić.
Odpowiedzi:
usuwa to duplikaty w miejscu, bez tworzenia nowej tabeli
ALTER IGNORE TABLE `table_name` ADD UNIQUE (title, SID)
uwaga: działa dobrze tylko wtedy, gdy indeks mieści się w pamięci
ALTER IGNORE
.
ALTER TABLE foo ENGINE MyISAM
żeby to obejść, po czym z powrotem wymieniłem silnik.
Załóżmy, że masz tabelę employee
z następującymi kolumnami:
employee (first_name, last_name, start_date)
Aby usunąć wiersze ze zduplikowaną first_name
kolumną:
delete
from employee using employee,
employee e1
where employee.id > e1.id
and employee.first_name = e1.first_name
employee
ze sobą dla jednego dopasowania indeksu i jedno >
sprawdzenie indeksu będzie powolne w przypadku dużych tabel. Czy nie byłoby lepiej, SELECT MAX(ID) FROM t GROUP BY unique
a potem JOIN
dokładnym dopasowaniem ID
do MAX(ID)
?
Następnie usuń duplikaty wszystkich identyfikatorów SID, nie tylko jednego.
Ze stołem temp
CREATE TABLE table_temp AS
SELECT * FROM table GROUP BY title, SID;
DROP TABLE table;
RENAME TABLE table_temp TO table;
Ponieważ temp_table
jest świeżo utworzony, nie ma indeksów. Musisz je odtworzyć po usunięciu duplikatów. Możesz sprawdzić, z jakimi indeksami masz w tabeliSHOW INDEXES IN table
Bez tabeli temp:
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;
Wszystko zależy od tego, jak dobrze wiesz, co robisz.
Utwórz tabelę i wstaw kilka wierszy:
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)
Usuń duplikaty w miejscu:
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)
Gotowe, zduplikowane wiersze są usuwane, zachowywany jest ostatni według sygnatury czasowej.
Nie masz timestamp
unikatowej kolumny indeksu do sortowania? Żyjesz w stanie degeneracji. Będziesz musiał wykonać dodatkowe czynności, aby usunąć zduplikowane wiersze.
utwórz tabelę pingwinów i dodaj kilka wierszy
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 |
# +------+----------+
zrobić klon pierwszej tabeli i skopiować do niej.
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 |
# +-----+------+----------+
Agregat max działa na nowym indeksie 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;
obserwować i porządkować
drop table penguins_copy;
select * from penguins;
+------+----------+
| foo | bar |
+------+----------+
| 1 | skipper |
| 3 | kowalski |
| 4 | rico |
+------+----------+
Elapsed: 1458.359 milliseconds
Co robi ta duża instrukcja usuwania SQL?
Pingwiny stołowe z aliasem „a” pozostają połączone z podzbiorem pingwinów stołowych zwanych aliasami „b”. W prawej tabeli „b”, która jest podzbiorem, znajduje się maksymalny znacznik czasu [lub max moo] pogrupowany według kolumn foo i bar. Jest to dopasowane do tabeli „a” po lewej stronie. (foo, bar, baz) po lewej stronie ma każdy wiersz w tabeli. Podzbiór „b” po prawej stronie ma (maxtimestamp, foo, bar), który jest dopasowywany do lewej tylko na tym, który JEST maksymalny.
Każdy wiersz inny niż maksymalna ma wartość maxtimestamp równą NULL. Przefiltruj te wiersze NULL i masz zestaw wszystkich wierszy pogrupowanych według foo i bar, który nie jest najnowszym bazem sygnatury czasowej. Usuń te.
Przed uruchomieniem wykonaj kopię zapasową tabeli.
Zapobiegaj ponownemu występowaniu tego problemu na tym stole:
Jeśli to zadziałało i ugasiło pożar w „zduplikowanym rzędzie”. Wspaniały. Teraz zdefiniuj nowy złożony unikatowy klucz w tabeli (w tych dwóch kolumnach), aby w pierwszej kolejności zapobiec dodawaniu większej liczby duplikatów.
Podobnie jak w przypadku dobrego układu odpornościowego, złe wiersze nie powinny nawet trafiać do stołu w momencie wstawiania. Później wszystkie programy dodające duplikaty będą emitować swój protest, a kiedy je naprawisz, ten problem nigdy się nie pojawi.
ID
kolumnę z automatycznym zwiększaniem, ON
klauzula musi tylko pasować do ID
kolumny, nic więcej.
Po tym, jak sam natknąłem się na ten problem, na ogromnej bazie danych, nie byłem pod wrażeniem wydajności żadnej z innych odpowiedzi. Chcę zachować tylko ostatni zduplikowany wiersz, a resztę usunąć.
W przypadku instrukcji z jednym zapytaniem, bez tabeli tymczasowej, działało to najlepiej,
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);
Jedynym zastrzeżeniem jest to, że muszę uruchamiać zapytanie wiele razy, ale mimo to okazało się, że działa lepiej niż inne opcje.
To zawsze wydaje się działać dla mnie:
CREATE TABLE NoDupeTable LIKE DupeTable;
INSERT NoDupeTable SELECT * FROM DupeTable group by CommonField1,CommonFieldN;
Który zachowuje najniższy identyfikator dla każdego z dupleksów i reszty rekordów bez dupleksu.
Podjąłem również następujące kroki, aby problem z kopiowaniem nie występował już po usunięciu:
CREATE TABLE NoDupeTable LIKE DupeTable;
Alter table NoDupeTable Add Unique `Unique` (CommonField1,CommonField2);
INSERT IGNORE NoDupeTable SELECT * FROM DupeTable;
Innymi słowy, tworzę duplikat pierwszej tabeli, dodaję unikalny indeks do pól, których nie chcę mieć duplikatów, a następnie wykonuję operację, Insert IGNORE
która ma tę zaletę, że nie zawodzi jak zwykleInsert
przy pierwszej próbie dodania zduplikowany rekord oparty na dwóch polach i raczej ignoruje takie rekordy.
Poruszając się dalej, niemożliwe staje się utworzenie jakichkolwiek zduplikowanych rekordów na podstawie tych dwóch pól.
ORDER BY
w, SELECT
aby mieć pewność, który rekord trafi do katalogu NoDupeTable
?
ORDER by ID Asc
nic nie zaszkodzi, więc i tak zmienię odpowiedź.
Select Max(ID)
a, Order by Max(ID)
ale wystarczy odwrócić kolejność wkładek. Wydaje mi się, że uzyskanie najwyższego identyfikatora wymagałoby bardziej złożonego łączenia typu select, ponieważ niezależnie od tego, jak zamówisz powyżej, będziesz pobierać wartości pól z niższego identyfikatora.
MAX(ID)
lub MIN(ID)
i zamiast *
w SELECT FROM DupeTable
chociaż, w przeciwnym razie po prostu otrzymasz ID
losowo jedną z kolumn . W rzeczywistości wiele SQL, a nawet MySQL, wymaga wywołania funkcji agregującej w każdej kolumnie, która nie została określona w GROUP BY
klauzuli.
ID,First,Last,Notes
i rekordami, 1,Bob,Smith,NULL
a 2,Bob,Smith,Arrears
następnie wykonanie a SELECT *Max(ID), First,Last,Notes FROM DupeTable group by First,Last
zwróciłoby ten sam rekord, 1, z wyjątkiem z innym identyfikatorem. Zwróci wartość Max (ID), 2,Bob,Smith,NULL
a Min (ID) 1,Bob,Smith,NULL
. Wydaje mi się, że uzyskanie drugiego rekordu z napisem „Zaległości” w notatkach wymaga połączenia.
Oto prosta odpowiedź:
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
Aby b
tylko musi porównać b.id
= a.id_field
zakładając field_id
to unikatowy identyfikator automatycznego przyrostu. tak a.field_being_repeated = b.field_being_repeated
jest obce. (również b.id_field
nie występuje w tym zapytaniu to b.id
.
To działa dla mnie, aby usunąć stare rekordy:
delete from table where id in
(select min(e.id)
from (select * from table) e
group by column1, column2
having count(*) > 1
);
Możesz zamienić min (e.id) na max (e.id), aby usunąć najnowsze rekordy.
delete p from
product p
inner join (
select max(id) as id, url from product
group by url
having count(*) > 1
) unik on unik.url = p.url and unik.id != p.id;
Powyżej znajduję rozwiązanie Wernera najwygodniejsze, ponieważ działa niezależnie od obecności klucza podstawowego, nie zadziera z tabelami, wykorzystuje przyszłościowy zwykły sql, jest bardzo zrozumiałe.
Jak wspomniałem w swoim komentarzu, to rozwiązanie nie zostało jednak właściwie wyjaśnione. Więc to jest moje, oparte na tym.
1) dodaj nową kolumnę logiczną
alter table mytable add tokeep boolean;
2) dodaj ograniczenie do zduplikowanych kolumn ORAZ nowej kolumny
alter table mytable add constraint preventdupe unique (mycol1, mycol2, tokeep);
3) ustaw kolumnę logiczną na wartość true. To się powiedzie tylko w jednym ze zduplikowanych wierszy z powodu nowego ograniczenia
update ignore mytable set tokeep = true;
4) usuń wiersze, które nie zostały oznaczone jako tokeep
delete from mytable where tokeep is null;
5) upuść dodaną kolumnę
alter table mytable drop tokeep;
Proponuję zachować dodane ograniczenie, aby w przyszłości uniknąć nowych duplikatów.
Ta procedura usunie wszystkie duplikaty (w tym wielokrotności) z tabeli, zachowując ostatni duplikat. Jest to rozszerzenie opcji Pobieranie ostatniego rekordu w każdej grupie
Mam nadzieję, że to komuś się przyda.
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);
Kolejny łatwy sposób ... używając UPDATE IGNORE:
Musisz użyć indeksu dla jednej lub więcej kolumn (indeks typu). Utwórz nową tymczasową kolumnę odniesienia (nie będącą częścią indeksu). W tej kolumnie oznaczasz unikaty w, aktualizując je klauzulą ignore. Krok po kroku:
Dodaj tymczasową kolumnę odniesienia, aby oznaczyć unikatów:
ALTER TABLE `yourtable` ADD `unique` VARCHAR(3) NOT NULL AFTER `lastcolname`;
=> to doda kolumnę do twojej tabeli.
Zaktualizuj tabelę, spróbuj oznaczyć wszystko jako unikalne, ale zignoruj możliwe błędy spowodowane problemem zduplikowanego klucza (rekordy zostaną pominięte):
UPDATE IGNORE `yourtable` SET `unique` = 'Yes' WHERE 1;
=> odkryjesz, że Twoje zduplikowane rekordy nie zostaną oznaczone jako unikalne = 'Tak', innymi słowy, tylko jeden z każdego zestawu zduplikowanych rekordów zostanie oznaczony jako unikalny.
Usuń wszystko, co nie jest unikalne:
DELETE * FROM `yourtable` WHERE `unique` <> 'Yes';
=> Spowoduje to usunięcie wszystkich zduplikowanych rekordów.
Upuść kolumnę ...
ALTER TABLE `yourtable` DROP `unique`;
unique
kolumna MUSI zostać dodana do unikalnego ograniczenia razem z kolumnami, które są obecnie zduplikowane, w przeciwnym razie całość nie zadziała, ponieważ SET unique
= 'Yes' nigdy nie zawiedzie.
unique
jest to słowo kluczowe mysql. Musi więc mieć znaki grawitacyjne (jak już są poprawnie wyświetlone). Użycie innego słowa na oznaczenie kolumny może być wygodniejsze.
Usuwanie duplikatów w tabelach MySQL jest częstym problemem, który zwykle wiąże się z określonymi potrzebami. Jeśli ktoś jest zainteresowany, tutaj ( Usuń zduplikowane wiersze w MySQL ) wyjaśniam, jak używać tabeli tymczasowej do usuwania duplikatów MySQL w niezawodny i szybki sposób, również do obsługi źródeł dużych zbiorów danych (z przykładami dla różnych przypadków użycia).
Ali , w twoim przypadku możesz uruchomić coś takiego:
-- 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;
Odpowiedź Love @ eric, ale wydaje się nie działać, jeśli masz naprawdę duży stół (dostaję, 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
gdy próbuję go uruchomić). Więc ograniczyłem zapytanie sprzężenia do rozważenia tylko zduplikowanych wierszy i otrzymałem:
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
Klauzula WHERE w tym przypadku umożliwia MySQL ignorowanie każdego wiersza, który nie ma duplikatu, a także ignoruje, jeśli jest to pierwsza instancja duplikatu, więc tylko kolejne duplikaty będą ignorowane. Zmień MIN(baz)
na, MAX(baz)
aby zachować ostatnią instancję zamiast pierwszej.
Działa to w przypadku dużych tabel:
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;
Aby usunąć najstarszą zmianę max(id)
wmin(id)
To tutaj spowoduje, że kolumna stanie column_name
się kluczem podstawowym, a tymczasem zignoruje wszystkie błędy. Więc usunie wiersze ze zduplikowaną wartością dla column_name
.
ALTER IGNORE TABLE `table_name` ADD PRIMARY KEY (`column_name`);
Myślę, że to zadziała, kopiując tabelę i opróżniając ją, a następnie umieszczając z powrotem tylko odrębne wartości, ale sprawdź to dokładnie przed wykonaniem tego na dużych ilościach danych.
Tworzy kopię Twojego stołu
utwórz tabelę temp_table jak stara nazwa tabeli; wstaw temp_table select * from oldtablename;
Opróżnia oryginalny stół
DELETE * from oldtablename;
Kopiuje wszystkie odrębne wartości ze skopiowanej tabeli z powrotem do oryginalnej tabeli
INSERT stara nazwa_tablicy SELECT * z grupy temp_table wg imienia, nazwiska, ur
Usuwa tabelę tymczasową.
Drop Table temp_table
Musisz pogrupować według wszystkich pól, które chcesz rozróżnić.
DELETE T2
FROM table_name T1
JOIN same_table_name T2 ON (T1.title = T2.title AND T1.ID <> T2.ID)
oto jak zwykle eliminuję duplikaty
Możesz po prostu użyć klauzuli DISTINCT, aby wybrać „wyczyszczoną” listę (a tutaj jest bardzo prosty przykład, jak to zrobić).
DISTINCT
Ciebie powoduje utratę wszelkich informacji o duplikatach, które mogłeś mieć w pierwszej kolejności. Czy możesz pokazać sposób usuwania duplikatów za jego pomocą?
Czy to zadziała, jeśli je policzysz, a następnie dodasz ograniczenie do zapytania usuwającego, pozostawiając tylko jedno?
Na przykład, jeśli masz dwa lub więcej, napisz zapytanie w ten sposób:
DELETE FROM table WHERE SID = 1 LIMIT 1;
Aby usunąć zduplikowane dane z tabeli, wystarczy wykonać kilka podstawowych czynności:
Oto pełny samouczek: https://blog.teamsql.io/deleting-duplicate-data-3541485b3473