Une méthode SQL
Tout d'abord, résolvons simplement le problème en SQL, afin que la syntaxe spécifique à Rails ne nous trompe pas.
Cette question SO est un parallèle assez clair :Trouver un doublon valeurs dans une table SQL
La réponse de KM (deuxième en partant du haut, non cochée, pour le moment) répond à vos critères de renvoi de tous les enregistrements en double avec leurs identifiants. J'ai modifié les KM's SQL pour correspondre à votre tableau...
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 partie à l'intérieur du INNER JOIN ( )
est essentiellement ce que vous avez déjà généré. Un tableau groupé de titres et de décomptes en double. L'astuce est JOIN
en l'intégrant aux movies
non modifiés table, qui exclura tous les films qui n'ont pas de correspondance dans la requête de dupes.
Pourquoi est-ce si difficile à générer dans Rails ? La partie la plus délicate est que, parce que nous sommes JOIN
regarder des movies
aux movies
, nous devons créer des alias de table (m
et dupes
dans ma requête ci-dessus).
Malheureusement, Rails ne fournit aucun moyen propre de déclarer ces alias. Quelques références :
- Problèmes liés à Rails GitHub en mentionnant "join" et "alias". Misère.
- SO Question :requête ActiveRecord avec table d'alias noms
Heureusement, puisque nous avons le SQL en main, nous pouvons utiliser le .find_by_sql
méthode...
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")
Parce que nous appelons Movie.find_by_sql
, ActiveRecord suppose que notre SQL écrit à la main peut être regroupé dans Movie
objets. Il ne masse ni ne génère quoi que ce soit, ce qui nous permet de faire nos alias.
Cette approche a ses défauts. Il renvoie un tableau et non une relation ActiveRecord, ce qui signifie qu'il ne peut pas être chaîné avec d'autres étendues. Et, dans la documentation de find_by_sql
méthode
, nous obtenons un découragement supplémentaire...
Une manière ferroviaire
Vraiment, que fait le SQL ci-dessus ? Il obtient une liste de noms qui apparaissent plus d'une fois. Ensuite, il fait correspondre cette liste à la table d'origine. Alors, faisons cela en utilisant Rails.
titles_with_multiple = Movie.group(:title).having("count(title) > 1").count.keys
Movie.where(title: titles_with_multiple)
Nous appelons .keys
car la première requête renvoie un hachage. Les clés sont nos titres. Le where()
peut prendre un tableau, et nous lui avons donné un tableau de titres. Gagnant.
Vous pourriez dire qu'une ligne de Ruby est plus élégante que deux. Et si cette seule ligne de Ruby contient une chaîne SQL impie, à quel point est-elle vraiment élégante ?
J'espère que cela vous aidera !