Ein SQL-y-Weg
Lassen Sie uns das Problem zuerst einfach in SQL lösen, damit uns die Rails-spezifische Syntax nicht austrickst.
Diese SO-Frage ist eine ziemlich klare Parallele:Duplikate finden Werte in einer SQL-Tabelle
Die Antwort von KM (Zweiter von oben, im Moment nicht angekreuzt) erfüllt Ihre Kriterien für die Rückgabe aller duplizierten Datensätze zusammen mit ihren IDs. Ich habe KMs modifiziert SQL, um Ihrem zu entsprechen Tabelle...
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
Der Teil innerhalb des INNER JOIN ( )
ist im Wesentlichen das, was Sie bereits generiert haben. Eine gruppierte Tabelle mit duplizierten Titeln und Anzahlen. Der Trick ist JOIN
es zu den unveränderten movies
hinzufügen Tabelle, die alle Filme ausschließt, die keine Übereinstimmungen in der Abfrage von Duplikaten haben.
Warum ist das in Rails so schwer zu generieren? Der schwierigste Teil ist das, weil wir JOIN
sind movies
ansehen zu movies
, müssen wir Tabellenaliase erstellen (m
und dupes
in meiner Abfrage oben).
Leider bietet it Rails keine sauberen Möglichkeiten, diese Aliase zu deklarieren. Einige Referenzen:
- Rails-GitHub-Probleme Erwähnung von "Join" und "Alias". Elend.
- SO-Frage:ActiveRecord-Abfrage mit Alias-Tabelle Namen
Glücklicherweise können wir, da wir das SQL in der Hand haben, .find_by_sql
verwenden 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")
Weil wir Movie.find_by_sql
aufrufen , geht ActiveRecord davon aus, dass unser handgeschriebenes SQL in Movie
gebündelt werden kann Objekte. Es massiert oder generiert nichts, wodurch wir unsere Aliase verwenden können.
Dieser Ansatz hat seine Mängel. Es gibt ein Array und keine ActiveRecord-Beziehung zurück, was bedeutet, dass es nicht mit anderen Bereichen verkettet werden kann. Und in der Dokumentation für find_by_sql
Methode
, bekommen wir zusätzliche Entmutigung...
A Rails-y Way
Wirklich, was macht das SQL oben? Es wird eine Liste mit Namen erstellt, die mehr als einmal vorkommen. Dann wird diese Liste mit der ursprünglichen Tabelle abgeglichen. Also machen wir das einfach mit Rails.
titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys
Movie.where(title: titles_with_multiple)
Wir nennen .keys
weil die erste Abfrage einen Hash zurückgibt. Die Schlüssel sind unsere Titel. Das where()
-Methode kann ein Array annehmen, und wir haben ihr ein Array von Titeln übergeben. Gewinner.
Man könnte argumentieren, dass eine Linie von Ruby eleganter ist als zwei. Und wenn diese eine Ruby-Zeile einen gottlosen SQL-String enthält, wie elegant ist das dann wirklich?
Hoffe, das hilft!