Sposób SQL-y
Najpierw po prostu rozwiążmy problem w SQL, aby składnia specyficzna dla Railsów nas nie oszukała.
To pytanie SO jest dość wyraźną paralelą:Znajdowanie duplikatu wartości w tabeli SQL
Odpowiedź od KM (druga od góry, obecnie niezaznaczona) spełnia Twoje kryteria zwrotu wszystkich zduplikowanych rekordów wraz z ich identyfikatorami. Zmodyfikowałem KM SQL pasujący do Twojego stół...
SELECT
m.id, m.title
FROM
movies m
INNER JOIN (
SELECT
title, COUNT(*) AS CountOf
FROM
movies
GROUP BY
title
HAVING COUNT(*)>1
) dupes
ON
m.title=dupes.title
Część wewnątrz INNER JOIN ( )
jest zasadniczo tym, co już wygenerowałeś. Zgrupowana tabela z powielonymi tytułami i liczbami. Sztuczka to JOIN
do niezmodyfikowanych movies
tabeli, która wyklucza wszystkie filmy, które nie mają dopasowań w zapytaniu duplikatów.
Dlaczego tak trudno to wygenerować w Railsach? Najtrudniejsze jest to, że jesteśmy JOIN
ing movies
do movies
, musimy utworzyć aliasy tabel (m
i dupes
w moim zapytaniu powyżej).
Niestety, it Rails nie zapewnia żadnych czystych sposobów deklarowania tych aliasów. Niektóre referencje:
- Problemy z Railsami GitHub wspominając „dołącz” i „alias”. Nędza.
- Pytanie SO:zapytanie ActiveRecord z tabelą aliasów nazwiska
Na szczęście, ponieważ mamy pod ręką SQL, możemy użyć .find_by_sql
metoda...
Movie.find_by_sql("SELECT m.id, m.title FROM movies m INNER JOIN (SELECT title, COUNT(*) FROM movies GROUP BY title HAVING COUNT(*)>1) dupes ON m.first=.first")
Ponieważ dzwonimy do Movie.find_by_sql
, ActiveRecord zakłada, że nasz ręcznie napisany kod SQL można umieścić w pakiecie Movie
przedmioty. Nie masuje ani nie generuje niczego, co pozwala nam tworzyć nasze aliasy.
Takie podejście ma swoje wady. Zwraca tablicę, a nie relację ActiveRecord, co oznacza, że nie można jej łączyć z innymi zakresami. Oraz w dokumentacji find_by_sql
metoda
, dostajemy dodatkowe zniechęcenie...
Sposób Rails-y
Naprawdę, co robi SQL powyżej? Pobiera listę nazwisk, które pojawiają się więcej niż raz. Następnie dopasowuje tę listę do oryginalnej tabeli. Zróbmy to po prostu za pomocą Railsów.
titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys
Movie.where(title: titles_with_multiple)
Nazywamy .keys
ponieważ pierwsze zapytanie zwraca hash. Kluczami są nasze tytuły. where()
Metoda może przyjąć tablicę, a my przekazaliśmy jej tablicę tytułów. Zwycięzca.
Można argumentować, że jedna linia Ruby jest bardziej elegancka niż dwie. A jeśli ta jedna linijka Rubiego ma osadzony w sobie bezbożny ciąg SQL, jak naprawdę jest elegancki?
Mam nadzieję, że to pomoże!