Ett SQL-y-sätt
Låt oss först lösa problemet i SQL, så att den Rails-specifika syntaxen inte lurar oss.
Denna SO-fråga är en ganska tydlig parallell:Hittar duplikat värden i en SQL-tabell
Svaret från KM (nästa från toppen, ej bockmarkerat, för tillfället) uppfyller dina kriterier för att returnera alla duplicerade poster tillsammans med deras ID. Jag har ändrat KM:er SQL för att matcha din bord...
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
Delen inuti INNER JOIN ( )
är i huvudsak vad du redan har genererat. En grupperad tabell med dubblerade titlar och antal. Tricket är JOIN
överföra den till de omodifierade movies
tabell, vilket kommer att utesluta alla filmer som inte har matchningar i frågan om duper.
Varför är detta så svårt att skapa i Rails? Det svåraste är det eftersom vi är JOIN
ing movies
till movies
måste vi skapa tabellalias (m
och dupes
i min fråga ovan).
Tyvärr tillhandahåller it Rails inga rena sätt att deklarera dessa alias. Några referenser:
- Rails GitHub-problem a> nämner "join" och "alias". Misär.
- SO-fråga:ActiveRecord-fråga med aliastabell namn
Lyckligtvis, eftersom vi har SQL i handen, kan vi använda .find_by_sql
metod...
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")
Eftersom vi kallar Movie.find_by_sql
, ActiveRecord antar att vår handskrivna SQL kan buntas in i Movie
objekt. Det masserar inte eller genererar något, vilket låter oss göra våra alias.
Detta tillvägagångssätt har sina brister. Den returnerar en array och inte en ActiveRecord Relation, vilket betyder att den inte kan kedjas med andra scopes. Och i dokumentationen för find_by_sql
metod
, vi blir extra missmodiga...
Ett spårlöst sätt
Verkligen, vad gör SQL ovan? Den får en lista med namn som visas mer än en gång. Sedan matchar den den listan mot den ursprungliga tabellen. Så låt oss bara göra det med Rails.
titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys
Movie.where(title: titles_with_multiple)
Vi anropar .keys
eftersom den första frågan returnerar en hash. Nycklarna är våra titlar. where()
Metoden kan ta en array, och vi har gett den en mängd titlar. Vinnare.
Du kan hävda att en linje av Ruby är mer elegant än två. Och om den där ena raden av Ruby har en ogudaktig sträng av SQL inbäddad i sig, hur elegant är den egentligen?
Hoppas detta hjälper!