web-dev-qa-db-fra.com

Rechercher des lignes avec plusieurs champs en double avec Active Record, Rails & Postgres

Quel est le meilleur moyen de trouver des enregistrements avec des valeurs en double sur plusieurs colonnes à l'aide de Postgres et d'Activerecord?

J'ai trouvé cette solution ici :

User.find(:all, :group => [:first, :email], :having => "count(*) > 1" )

Mais cela ne semble pas fonctionner avec postgres. Je reçois cette erreur:

PG :: GroupingError: ERROR: la colonne "parties.id" doit apparaître dans la clause GROUP BY ou être utilisée dans une fonction d'agrégat

77
newUserNameHere

Version testée & de travail

User.select(:first,:email).group(:first,:email).having("count(*) > 1")

En outre, ceci est un peu sans rapport, mais pratique. Si vous voulez voir combien de fois chaque combinaison a été trouvée, mettez .size à la fin:

User.select(:first,:email).group(:first,:email).having("count(*) > 1").size

et vous obtiendrez un résultat en retrait qui ressemble à ceci:

{[nil, nil]=>512,
 ["Joe", "[email protected]"]=>23,
 ["Jim", "[email protected]"]=>36,
 ["John", "[email protected]"]=>21}

Je pensais que c'était plutôt cool et je ne l'avais jamais vu auparavant.

Merci à Taryn, il ne s'agit que d'une version modifiée de sa réponse.

169
newUserNameHere

Cette erreur se produit car POSTGRES nécessite que vous placiez des colonnes de regroupement dans la clause SELECT.

essayer:

User.select(:first,:email).group(:first,:email).having("count(*) > 1").all

(note: non testé, vous devrez peut-être le modifier)

EDITED pour supprimer la colonne id

25
Taryn East

Si vous avez besoin des modèles complets, essayez ce qui suit (basé sur la réponse de @ newUserNameHere).

User.where(email: User.select(:email).group(:email).having("count(*) > 1").select(:email))

Cela renverra les lignes où l'adresse électronique de la ligne n'est pas unique.

Je ne suis pas au courant d'un moyen de faire cela sur plusieurs attributs.

6
Ben Aubin

Obtenez tous les doublons avec une requête single si vous utilisez PostgreSQL:

def duplicated_users
  duplicated_ids = User
    .group(:first, :email)
    .having("COUNT(*) > 1")
    .select('unnest((array_agg("id"))[2:])')

  User.where(id: duplicated_ids)
end

irb> duplicated_users
0
itsnikolay