En SQL-y måde
Først, lad os lige løse problemet i SQL, så den Rails-specifikke syntaks ikke narre os.
Dette SO-spørgsmål er en ret klar parallel:Find duplikat værdier i en SQL-tabel
Svaret fra KM (anden fra toppen, ikke markeret, i øjeblikket) opfylder dine kriterier for at returnere alle duplikerede poster sammen med deres ID'er. Jeg har ændret KM'er SQL, der matcher 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 inde i INNER JOIN ( )
er i bund og grund det, du allerede har genereret. En grupperet tabel med duplikerede titler og tæller. Tricket er JOIN
ing det til de umodificerede movies
tabel, som vil udelukke alle film, der ikke har matches i forespørgslen om duper.
Hvorfor er det så svært at generere i Rails? Den sværeste del er det, fordi vi er JOIN
ved at bruge movies
til movies
, skal vi oprette tabelaliasser (m
og dupes
i min forespørgsel ovenfor).
Desværre giver it Rails ikke nogen rene måder at erklære disse aliaser på. Nogle referencer:
- Problemer med Rails GitHub a> omtale "join" og "alias". Elendighed.
- SO-spørgsmål:ActiveRecord-forespørgsel med alias-tabel navne
Heldigvis, da vi har SQL i hånden, kan vi bruge .find_by_sql
metode...
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")
Fordi vi kalder Movie.find_by_sql
, ActiveRecord antager, at vores håndskrevne SQL kan samles i Movie
genstande. Det masserer eller genererer ikke noget, hvilket lader os lave vores aliaser.
Denne tilgang har sine mangler. Det returnerer et array og ikke en ActiveRecord Relation, hvilket betyder, at det ikke kan kædes sammen med andre scopes. Og i dokumentationen til find_by_sql
metode
, får vi ekstra modløshed...
En skinnende vej
Virkelig, hvad laver SQL'en ovenfor? Den får en liste over navne, der vises mere end én gang. Så matcher den denne liste mod den originale tabel. Så lad os bare gøre det ved at bruge Rails.
titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys
Movie.where(title: titles_with_multiple)
Vi kalder .keys
fordi den første forespørgsel returnerer en hash. Nøglerne er vores titler. where()
metode kan tage en række, og vi har givet den en række titler. Vinder.
Du kan argumentere for, at en linje af Ruby er mere elegant end to. Og hvis den ene linje af Ruby har en ugudelig streng af SQL indlejret i sig, hvor elegant er den så egentlig?
Håber dette hjælper!