Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Usuwanie duplikatów z 1,6mln rekordów
Forum PHP.pl > Forum > Bazy danych > MySQL
1q2w3e4r
Witam,
Muszę usunąć duplikaty z 1,6mln rekordów.. Zastanawiam się jak to zrobić najszybciej.
Duplikaty mają się usuwać wierszami, czyli duplikaty jeśli jeden wiersz nie może się równać innemu.

Macie jakieś propozycje jak to zrobić?

Proszę o szybka odpowiedź.
toniq
pobierasz wszystkie posortowane po tym co chcesz
sprawdzasz czy aktualna linia jest równa poprzedniej jeśli tak to ja kasujesz

puf kilka sekund później baza bez potworzeń
1q2w3e4r
Tak, ale to jest 1 600 000 rekordów, każdy przez każdy to by sie to wykonywało strasznie długo.
Najlepiej by to było zrobić jednym zapytaniem aby wydobyć powtórzenia. A tutaj nie sprawdzamy pojedyńczych kolumn tylko jako całość. Czyli czy cały wiersz jest podobny do całego innego.
alegorn
duplikaty? w sensie na caly rekord? czy na konkretne pole?

najszybciej - tzn najmniej zabawy - to skopiuj strukture do nowej tabeli obok,
zaloz index unique na to co ma byc unikatem.
z zapusc
  1. INSERT IGNORE INTO nowa tabela
  2. SELECT * FROM stara tabela



po czasie jakim bedzie to potrzebne i wydajne dla twojego kompa, usuwasz stara tabele, zmieniasz nazwe w nowej i juz
masz tabele, unikaty, i extra - masz zoptymalizowana tabele (tzn brak dziur w indexach)


moze byc?
jacek.
cojack
Z http://dev.mysql.com/doc/refman/5.0/en/delete.html lekko przerobione:

Cytat
If you are deleting many rows from a large table, you may exceed the lock table size for an InnoDB table. To avoid this problem, or simply to minimize the time that the table remains locked, the following strategy (which does not use DELETE at all) might be helpful:

Select the rows not to be deleted into an empty table that has the same structure as the original table:

Kod
INSERT INTO t_copy SELECT DISTINCT * FROM t;

Use RENAME TABLE to atomically move the original table out of the way and rename the copy to the original name:

Kod
RENAME TABLE t TO t_old, t_copy TO t;


Drop the original table:

Kod
DROP TABLE t_old;
alegorn
@cojak w zasadzie to tosamo co i ja zaproponowalem. zastanawialem sie nad tym co bardziej optymalne, de facto, i chyba jednak moje rozwiazanie daje wieksze mozliwosci.
przewaga chyba jest w tym ze wybieram pola jakie tworza unikalny index, np jesli w tabeli masz inkrementacje, lub kolumne typu creation_at, nawet wtedy pomimo unikalnosci wiesza, jestem w stanie wyłuskać duplikaty :]

choc to juz teoretyzowanie, oba rozwiazania opieraja sie na dokladnie tej samej zasadzie, roznia sie jedynie drobnym detalem.

j.
toniq
wersja z php ma pewna zaletę ze możesz dowolnie filtrować te wyniki


  1. $query = mysql_query('SELECT * FROM tab ORDER BY wiersz DESC');//ważne zęby były posortowane wyniki
  2. $i=0;
  3. while ($row = mysql_fetch_array($query)) {
  4. if ($i > 0)//pomijamy 1 wiersz :)
  5. {
  6. if ($p == $row['wiersz'])//tu dowone sprawdzanie tego wiersza
  7. mysql_query('DELETE FROM tab WHERE id = '.$row['id']);
  8. }
  9. $p = $row['wiersz'];
  10. $i++;
  11. }


i jesli tylko jest index na tym co chcesz sprawdzac to wykonanie tego kodu to kilka sekund smile.gif
alegorn
toniq:: kilka sekund?? dyskusyjne. no ale nawet gdyby, to czy zastanawiales sie jak po takiej operacji wyglada index?
ser szwajcarski przy nim to malo... i to taki duuuzy ser wink.gif
jasne ze to sie da zalatwic kolejnymi operacjami, ale...wink.gif

poza tym, wybranie tak okolo 1,6 kk rekordow, kazdy minimum z kilkoma, albo kilkunastu polami... wyobrazasz sobie ile to pamieci potrzebuje? ja mam kilka baz - ktorych w taki sposob nie dasz rady zaczytac...
wiec wprowadzasz w kolejnym kroku limitowanie...?
a jak sie nie pogubisz w tym co juz pobrales a co nie? w sensie ze np. z pierwszej partii 1000 rekordow wywaliles np 100 pol, to czy pobierasz 'limit 1000,1000' czy tez 'limit ' . (1000 - $ilosc_usunietych) . ',1000'

no, jasne ze da sie to napisac w phpie, ale stopien skomplikowania, i jakosc koncowa - pozostawia sporo do zyczenia smile.gif


j.

ps swoja droga - masz 'ciekawy' styl pisania kodu.. poczytaj sobie o stylach kodowania - to zaprocentuje w przyszlosci smile.gif << to rada, nie krytyka :]
cudny
Cytat(toniq @ 15.03.2012, 08:00:12 ) *
wersja z php ma pewna zaletę ze możesz dowolnie filtrować te wyniki


  1. $query = mysql_query('SELECT * FROM tab ORDER BY wiersz DESC');//ważne zęby były posortowane wyniki
  2. $i=0;
  3. while ($row = mysql_fetch_array($query)) {
  4. if ($i > 0)//pomijamy 1 wiersz :)
  5. {
  6. if ($p == $row['wiersz'])//tu dowone sprawdzanie tego wiersza
  7. mysql_query('DELETE FROM tab WHERE id = '.$row['id']);
  8. }
  9. $p = $row['wiersz'];
  10. $i++;
  11. }


i jesli tylko jest index na tym co chcesz sprawdzac to wykonanie tego kodu to kilka sekund smile.gif


Nie obraź się ale to co napisałeś to totalna bzdura i nieporozumienie biggrin.gif

Co do samego rozwiązania problemu to są dwie opcje (tym bardziej, że z tego co piszesz to jednorazowe rozwiązanie, więc ja bym się zbytnio nad wydajnością nie zastanawiał - ma zadziałać, a sql bajecznie szybko radzi sobie z dużymi ilościami rekordów):

Albo robisz to co cojack i alegorn napisali (wszytko jedno co smile.gif ) albo sprawdź czy parser mysql pozwoli na cos takiego:

  1. DELETE FROM db WHERE id NOT IN(SELECT id FROM db GROUP BY kolumna_ktora_sie_bedzie_powtarzac)

To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2024 Invision Power Services, Inc.