Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Usuwanie duplikatów z 1,6mln rekordów
1q2w3e4r
post 13.03.2012, 17:57:54
Post #1





Grupa: Zarejestrowani
Postów: 238
Pomógł: 0
Dołączył: 6.05.2011

Ostrzeżenie: (10%)
X----


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ź.
Go to the top of the page
+Quote Post
toniq
post 13.03.2012, 18:35:55
Post #2





Grupa: Zarejestrowani
Postów: 3
Pomógł: 0
Dołączył: 20.01.2012

Ostrzeżenie: (0%)
-----


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ń
Go to the top of the page
+Quote Post
1q2w3e4r
post 14.03.2012, 14:46:02
Post #3





Grupa: Zarejestrowani
Postów: 238
Pomógł: 0
Dołączył: 6.05.2011

Ostrzeżenie: (10%)
X----


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.
Go to the top of the page
+Quote Post
alegorn
post 14.03.2012, 15:05:59
Post #4





Grupa: Zarejestrowani
Postów: 341
Pomógł: 40
Dołączył: 23.06.2009

Ostrzeżenie: (0%)
-----


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.

Ten post edytował alegorn 14.03.2012, 15:06:50
Go to the top of the page
+Quote Post
cojack
post 14.03.2012, 16:02:43
Post #5





Grupa: Zarejestrowani
Postów: 898
Pomógł: 80
Dołączył: 31.05.2008

Ostrzeżenie: (20%)
X----


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;


--------------------
cojack blog - mój blog (na jakiś czas off).
"jak czegoś nie wiem, to nie myślę że wiem" - moja domena
Go to the top of the page
+Quote Post
alegorn
post 14.03.2012, 17:09:31
Post #6





Grupa: Zarejestrowani
Postów: 341
Pomógł: 40
Dołączył: 23.06.2009

Ostrzeżenie: (0%)
-----


@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.
Go to the top of the page
+Quote Post
toniq
post 15.03.2012, 08:00:12
Post #7





Grupa: Zarejestrowani
Postów: 3
Pomógł: 0
Dołączył: 20.01.2012

Ostrzeżenie: (0%)
-----


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
Go to the top of the page
+Quote Post
alegorn
post 15.03.2012, 10:56:26
Post #8





Grupa: Zarejestrowani
Postów: 341
Pomógł: 40
Dołączył: 23.06.2009

Ostrzeżenie: (0%)
-----


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 :]

Ten post edytował alegorn 15.03.2012, 11:04:32
Go to the top of the page
+Quote Post
cudny
post 15.03.2012, 21:48:11
Post #9





Grupa: Zarejestrowani
Postów: 387
Pomógł: 66
Dołączył: 31.03.2005
Skąd: Kielce

Ostrzeżenie: (0%)
-----


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)



--------------------
..::: Jak pomogłem to kliknij pomógł. Tak rzadko używacie tej opcji :( :::..
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 26.04.2025 - 00:36