Un modo SQL-y
Innanzitutto, risolviamo il problema in SQL, in modo che la sintassi specifica di Rails non ci inganni.
Questa domanda SO è un parallelo abbastanza chiaro:Trovare duplicati valori in una tabella SQL
La risposta di KM (seconda dall'alto, non contrassegnata, al momento) soddisfa i tuoi criteri di restituzione di tutti i record duplicati insieme ai loro ID. Ho modificato KM SQL in modo che corrisponda a tuo tavola...
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
La parte all'interno di INNER JOIN ( )
è essenzialmente ciò che hai già generato. Una tabella raggruppata di titoli e conteggi duplicati. Il trucco è JOIN
inviandolo ai movies
non modificati tabella, che escluderà tutti i film che non hanno corrispondenze nella query dei duplicati.
Perché è così difficile da generare in Rails? La parte più complicata è che, perché siamo JOIN
in movies
a movies
, dobbiamo creare alias di tabella (m
e dupes
nella mia domanda sopra).
Purtroppo, it Rails non fornisce alcun modo pulito per dichiarare questi alias. Alcuni riferimenti:
- Problemi con Rails GitHub citando "join" e "alias". Miseria.
- Domanda SO:Query ActiveRecord con tabella alias nomi
Fortunatamente, dato che abbiamo l'SQL in mano, possiamo usare .find_by_sql
metodo...
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")
Perché stiamo chiamando Movie.find_by_sql
, ActiveRecord presuppone che il nostro SQL scritto a mano possa essere raggruppato in Movie
oggetti. Non massaggia né genera nulla, il che ci consente di fare i nostri alias.
Questo approccio ha i suoi difetti. Restituisce una matrice e non una relazione ActiveRecord, il che significa che non può essere concatenato con altri ambiti. E, nella documentazione per find_by_sql
metodo
, otteniamo ulteriore scoraggiamento...
Un modo Rails-y
Davvero, cosa sta facendo l'SQL sopra? Sta ottenendo un elenco di nomi che appaiono più di una volta. Quindi, sta abbinando quell'elenco alla tabella originale. Quindi, facciamolo usando Rails.
titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys
Movie.where(title: titles_with_multiple)
Chiamiamo .keys
perché la prima query restituisce un hash. Le chiavi sono i nostri titoli. Il where()
il metodo può richiedere un array e gli abbiamo consegnato un array di titoli. Vincitore.
Si potrebbe obiettare che una riga di Ruby è più elegante di due. E se quell'unica riga di Ruby ha una stringa empia di SQL incorporata, quanto è davvero elegante?
Spero che questo aiuti!