Rails: sélectionnez des valeurs uniques dans une colonne

238

J'ai déjà une solution qui fonctionne, mais j'aimerais vraiment savoir pourquoi cela ne fonctionne pas:

ratings = Model.select(:rating).uniq
ratings.each { |r| puts r.rating }

Il sélectionne, mais n'imprime pas de valeurs uniques, il imprime toutes les valeurs, y compris les doublons. Et c'est dans la documentation: http://guides.rubyonrails.org/active_record_querying.html#selecting-specific-fields

alexandrecosta
la source
2
Un autre exemple avec uniq stackoverflow.com/questions/8369812/…
manish nautiyal

Réponses:

449
Model.select(:rating)

Le résultat de ceci est une collection d' Modelobjets. Pas des évaluations simples. Et du uniqpoint de vue de, ils sont complètement différents. Vous pouvez utiliser ceci:

Model.select(:rating).map(&:rating).uniq

ou ceci (le plus efficace)

Model.uniq.pluck(:rating)

# rails 5+
Model.distinct.pluck(:rating)

Mettre à jour

Apparemment, depuis les rails 5.0.0.1, il ne fonctionne que sur les requêtes de "haut niveau", comme ci-dessus. Ne fonctionne pas sur les proxys de collection (relations "has_many", par exemple).

Address.distinct.pluck(:city) # => ['Moscow']
user.addresses.distinct.pluck(:city) # => ['Moscow', 'Moscow', 'Moscow']

Dans ce cas, dédupliquez après la requête

user.addresses.pluck(:city).uniq # => ['Moscow']
Sergio Tulentsev
la source
J'ai fait un: group (: rating) .collect {| r | r.rating} Depuis map == collect, où puis-je lire sur cette sintax que vous avez utilisée (&: rating)? Je ne vois pas cela dans la documentation de Ruby.
alexandrecosta
@ user1261084: voir le symbole # to_proc pour comprendre .map (&: rating). PragDave explique
dbenhur
63
Il convient de noter que Model.uniq.pluck(:rating)c'est le moyen le plus efficace de le faire - cela génère du SQL qui utilise SELECT DISTINCTplutôt que de s'appliquer .uniqà un tableau
Mikey
23
Dans Rails 5, Model.uniq.pluck(:rating)seraModel.distinct.pluck(:rating)
neurodynamique
2
Si vous souhaitez sélectionner des valeurs uniques dans la relation has_many, vous pouvez toujours le faireModel.related_records.group(:some_column).pluck(:some_column)
Krzysztof Karski
92

Si vous allez utiliser Model.select, vous pouvez tout aussi bien l'utiliser DISTINCT, car il ne renverra que les valeurs uniques. C'est mieux car cela signifie qu'il renvoie moins de lignes et devrait être légèrement plus rapide que de renvoyer un certain nombre de lignes et de dire ensuite à Rails de choisir les valeurs uniques.

Model.select('DISTINCT rating')

Bien sûr, cela est fourni à condition que votre base de données comprenne le DISTINCTmot - clé, et la plupart le devraient.

kakubei
la source
6
Model.select("DISTINCT rating").map(&:rating)pour obtenir une gamme de notes uniquement.
Kris
Idéal pour ceux qui ont des applications héritées utilisant Rails 2.3
Mikey
3
Oui, cela fonctionne à merveille, mais il ne renvoie que l'attribut DISTINCT. Comment pouvez-vous renvoyer l'intégralité de l'objet Model tant qu'il est distinct? Pour que vous ayez accès à tous les attributs du modèle dans les cas où l'attribut est unique.
zero_cool
@Jackson_Sandland Si vous voulez un objet Model, celui-ci devra être instancié à partir d'un enregistrement dans la table. Mais vous ne sélectionnez pas un enregistrement uniquement une valeur unique (à partir de ce qui peut être plusieurs enregistrements).
Benissimo
69

Cela fonctionne aussi.

Model.pluck("DISTINCT rating")
Nat
la source
Je crois que pluck est Ruby 1.9.x et plus. Toute personne utilisant une version précédente ne l'aura pas. Si vous êtes en 1.9x et au-dessus, les docs rubis disent que cela fonctionne aussi: Model.uniq.pluck (: rating)
kakubei
6
pluckest une pure méthode Rails> 3.2 qui ne dépend pas de Ruby 1.9.x Voir apidock.com/rails/v3.2.1/ActiveRecord/Calculations/pluck
Daniel Rikowski
34

Si vous souhaitez également sélectionner des champs supplémentaires:

Model.select('DISTINCT ON (models.ratings) models.ratings, models.id').map { |m| [m.id, m.ratings] }
Marcin Nowicki
la source
1
select extra fields<3 <3
cappie013
27
Model.uniq.pluck(:rating)

# SELECT DISTINCT "models"."rating" FROM "models"

Cela a l'avantage de ne pas utiliser de chaînes sql et de ne pas instancier de modèles

Cameron Martin
la source
3
Cela génère une erreur avec Rails 5.1 / AR 5.1 => méthode non définie `uniq '
Graham Slick
24
Model.select(:rating).uniq

Ce code fonctionne comme 'DISTINCT' (pas comme Array # uniq) depuis rails 3.2

kuboon
la source
5
Model.select(:rating).distinct
hassan_i
la source
2
C'est la seule réponse officiellement correcte qui est également super efficace. Cependant, l'ajout .pluck(:rating)à la fin fera exactement ce que le PO a demandé.
Sheharyar
5

Si je vais dans le bon sens, alors:

Requête en cours

Model.select(:rating)

renvoie un tableau d'objets et vous avez écrit une requête

Model.select(:rating).uniq

uniq est appliqué sur un tableau d'objets et chaque objet a un identifiant unique. uniq exécute son travail correctement car chaque objet du tableau est uniq.

Il existe plusieurs façons de sélectionner une note distincte:

Model.select('distinct rating').map(&:rating)

ou

Model.select('distinct rating').collect(&:rating)

ou

Model.select(:rating).map(&:rating).uniq

ou

Model.select(:name).collect(&:rating).uniq

Encore une chose, première et deuxième requête: trouver des données distinctes par requête SQL.

Ces requêtes seront considérées comme "londres" et "londres" de la même manière qu'elles négligeront l'espace, c'est pourquoi elles sélectionneront "londres" une fois dans le résultat de votre requête.

Troisième et quatrième requête:

rechercher des données par requête SQL et pour des données distinctes, appliquez ruby ​​uniq mehtod. ces requêtes seront considérées comme "londres" et "londres" différentes, c'est pourquoi il sélectionnera "londres" et "londres" dans le résultat de votre requête.

s'il vous plaît préférez à l'image ci-jointe pour plus de compréhension et jetez un œil sur "Toured / Awaiting RFP".

entrez la description de l'image ici

uma
la source
6
map& collectsont des alias pour la même méthode, il n'est pas nécessaire de fournir des exemples pour les deux.
Adam Lassek
4

Certaines réponses ne prennent pas en compte l'OP veut un tableau de valeurs

D'autres réponses ne fonctionnent pas bien si votre modèle a des milliers d'enregistrements

Cela dit, je pense qu'une bonne réponse est:

    Model.uniq.select(:ratings).map(&:ratings)
    => "SELECT DISTINCT ratings FROM `models` " 

Parce que, tout d'abord, vous générez un tableau de modèles (avec une taille réduite en raison de la sélection), puis vous extrayez le seul attribut de ces modèles sélectionnés (évaluations)

Fernando Fabreti
la source
3

Si quelqu'un cherche la même chose avec Mongoid, c'est

Model.distinct(:rating)
Vassilis
la source
celui-ci ne fonctionne pas maintenant, il renvoie maintenant des multiples.
EUPHORAY
ne revient pas distinct
dowi
2

Une autre façon de collecter des colonnes uniq avec sql:

Model.group(:rating).pluck(:rating)
Slava Zharkov
la source