PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Renvoyer les enregistrements en double (activerecord, postgres)

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 :

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 !