Jak pobrać tylko i wyłącznie rekordy ze zduplikowaną kolumną

Nie prowadzę bloga, więc wrzucam na forum, może się komuś przyda.

Czasem jest tak że chcielibyśmy się pozbyć duplikatów z bazy. Język SQL nie posiada żadnej funckji/procedury która pozwala na wyszukiwanie rekordów na podstawie duplikatów, przydała by się jakaś odwrotnosc DISTINCT albo DISTINCTROW. Niestety musimy sobie jakos radzić sami. Jak to osiągnąć?
Są dwa sposoby:

Sposób 1:

SELECT * FROM tabela WHERE kolumna IN (SELECT kolumna FROM tabela GROUP BY kolumna HAVING COUNT(*) > 1)

Bardzo niewydajny (5 tys rekordów, bez indeksów… po 3 minutach odpuściłem sobie), operacja “SELECT kolumna FROM tabela GROUP BY kolumna HEVING COUNT(*) > 1” będzie wykonywana na każdym wierszu w tabeli. Można oczywiście rozbić to na dwa zapytania, pierwsze pobierające wszystkie zduplikowane wartości, drugie pobierające wszystkie rekordy ze zduplikowaną wartościa:

@duplicates = ActiveRecord::Base.connection.select_all("SELECT kolumna FROM tabela GROUP BY kolumna HAVING COUNT(*) > 1").map { |r| r["kolumna"] } Model.find(:all, :conditions => ["kolumna IN(?)",@duplicates.join(",")])
Pomimo tego sposób jest nadal bardzo nieelegandzki i niewydajny.

Sposób 2:

SELECT tabela.* FROM tabela INNER JOIN ( SELECT kolumna FROM kolumna GROUP BY kolumna HAVING count(*) > 1 ) duplicates on tabela.kolumna = duplicates.kolumna
Tutaj jeśli chodzi o wydajność sprawa ma się znacznie lepiej. Nasze zapytanie grupujące wykonane jest tylko raz. Na 5 tys rekordów przeszukanie bazy zajmuje 0.03 sec (bez indeksów). Najciekawszą rzeczą w 2 sposobie jest chyba użycie go w metodzie find, dzięki :joins jest to bardzo łatwe:

Model.find(:all, :joins => "INNER JOIN (SELECT kolumna FROM tabela GROUP BY kolumna HAVING COUNT(*) > 1) duplicates on duplicates.kolumna = models.kolumna")

W przypadku Oracla istnieje chyba jeszcze możliwość wykorzystania OVER i PARTITION BY, niestety ja oracla nie używam, pozatym sposób drugi zadziała szybko praktycznie na każdej badzie danych sql, a przedewszystkim łatwo się integruje z ActiveRecord.

Dzięki :smiley:

Natchnąłeś mnie tym na napisanie jednego chorego zapytania na 20 linijek, które znacząco przyspiesza pracę obecnie dzierganej aplikacji :slight_smile: