Een SQL-achtige manier
Laten we eerst het probleem in SQL oplossen, zodat de Rails-specifieke syntaxis ons niet voor de gek houdt.
Deze SO-vraag is een vrij duidelijke parallel:Duplicate vinden waarden in een SQL-tabel
Het antwoord van KM (tweede van boven, op dit moment niet aangevinkt) voldoet aan uw criteria om alle dubbele records samen met hun ID's te retourneren. Ik heb KM's gewijzigd SQL die overeenkomt met uw tafel...
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
Het gedeelte binnen de INNER JOIN ( )
is in wezen wat je al hebt gegenereerd. Een gegroepeerde tabel met dubbele titels en tellingen. De truc is JOIN
naar de ongewijzigde movies
tabel, die alle films uitsluit die geen overeenkomsten hebben in de zoekopdracht naar dupes.
Waarom is dit zo moeilijk te genereren in Rails? Het lastigste is dat, want we zijn JOIN
movies
spelen naar movies
, moeten we tabelaliassen maken (m
en dupes
in mijn vraag hierboven).
Helaas biedt het Rails geen schone manieren om deze aliassen te declareren. Enkele referenties:
- GitHub-problemen met rails met vermelding van "join" en "alias". Ellende.
- SO Vraag:ActiveRecord-query met alias'd tabel namen
Gelukkig, aangezien we de SQL in de hand hebben, kunnen we de .find_by_sql
gebruiken methode...
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")
Omdat we Movie.find_by_sql
. noemen , gaat ActiveRecord ervan uit dat onze handgeschreven SQL kan worden gebundeld in Movie
voorwerpen. Het masseert of genereert niets, waardoor we onze aliassen kunnen gebruiken.
Deze aanpak heeft zijn tekortkomingen. Het retourneert een array en geen ActiveRecord-relatie, wat betekent dat het niet kan worden gekoppeld aan andere scopes. En, in de documentatie voor de find_by_sql
methode
, krijgen we extra ontmoediging...
A Rails-y Way
Echt, wat doet de SQL hierboven? Het krijgt een lijst met namen die meer dan eens voorkomen. Vervolgens vergelijkt het die lijst met de originele tabel. Laten we dat dus gewoon doen met Rails.
titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys
Movie.where(title: titles_with_multiple)
We noemen .keys
omdat de eerste query een hash retourneert. De sleutels zijn onze titels. De where()
methode kan een array aannemen, en we hebben het een array van titels gegeven. Winnaar.
Je zou kunnen stellen dat één regel Ruby eleganter is dan twee. En als die ene regel Ruby een goddeloze reeks SQL bevat, hoe elegant is het dan eigenlijk?
Ik hoop dat dit helpt!