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


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']

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.


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


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] }

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


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


5
Model.select(:rating).distinct

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


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)


3

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

Model.distinct(:rating)

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)
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.