Trilhos: selecione valores exclusivos de uma coluna

238

Eu já tenho uma solução funcional, mas gostaria muito de saber por que isso não funciona:

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

Ele seleciona, mas não imprime valores exclusivos, imprime todos os valores, incluindo as duplicatas. E está na documentação: http://guides.rubyonrails.org/active_record_querying.html#selecting-specific-fields

alexandrecosta
fonte
2
Outro exemplo com o uniq stackoverflow.com/questions/8369812/…
manish nautiyal

Respostas:

449
Model.select(:rating)

O resultado disso é uma coleção de Modelobjetos. Não classificações simples. E do uniqponto de vista, eles são completamente diferentes. Você pode usar isto:

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

ou isso (mais eficiente)

Model.uniq.pluck(:rating)

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

Atualizar

Aparentemente, a partir do rails 5.0.0.1, ele funciona apenas em consultas de "nível superior", como acima. Não funciona em proxies de coleção (relações "has_many", por exemplo).

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

Nesse caso, desduplicar após a consulta

user.addresses.pluck(:city).uniq # => ['Moscow']
Sergio Tulentsev
fonte
Eu fiz um: group (: rating) .collect {| r | r.rating} Como map == collect, onde posso ler sobre essa sintaxe que você usou (&: rating)? Não vejo isso na documentação do Ruby.
11111 alexandrecosta
@ user1261084: consulte o símbolo # to_proc para entender .map (&: rating). PragDave explica
dbenhur
63
É importante notar que Model.uniq.pluck(:rating)é a maneira mais eficiente de fazer isso - isso gera SQL que usam SELECT DISTINCTem vez de aplicar .uniqpara uma matriz
Mikey
23
No Rails 5, Model.uniq.pluck(:rating)seráModel.distinct.pluck(:rating)
neurodinâmica 23/02
2
Se você deseja selecionar valores únicos do relacionamento has_many, sempre pode fazê-lo.Model.related_records.group(:some_column).pluck(:some_column)
Krzysztof Karski
92

Se você for usar Model.select, poderá usar apenas DISTINCT, pois retornará apenas os valores exclusivos. Isso é melhor porque significa que ele retorna menos linhas e deve ser um pouco mais rápido do que retornar um número de linhas e depois pedir ao Rails para escolher os valores exclusivos.

Model.select('DISTINCT rating')

Obviamente, isso é fornecido desde que o banco de dados entenda a DISTINCTpalavra - chave e a maioria deve.

kakubei
fonte
6
Model.select("DISTINCT rating").map(&:rating)para obter uma matriz apenas das classificações.
Kris
Grande para aqueles com aplicações legadas usando Rails 2.3
Mikey
3
Sim ... isso funciona de maneira fantástica - no entanto, mas apenas retorna o atributo DISTINCT. Como você pode retornar o objeto Model inteiro, contanto que seja distinto? Para que você tenha acesso a todos os atributos no modelo nas instâncias em que o atributo é exclusivo.
zero_cool
@Jackson_Sandland Se você deseja um objeto Model, isso precisa ser instanciado a partir de um registro na tabela. Mas você não está selecionando um registro apenas com um valor único (do que podem ser vários registros).
Benissimo
69

Isso também funciona.

Model.pluck("DISTINCT rating")
Nat
fonte
Eu acredito que o pluck é o Ruby 1.9.xe para cima. Quem usa uma versão anterior não a terá. Se você está em 1,9x e acima, os documentos rubi dizer isso também funciona: Model.uniq.pluck (: classificação)
kakubei
6
plucké um Rails puros> 3.2 método que não tem nenhuma dependência de rubi 1.9.x Veja apidock.com/rails/v3.2.1/ActiveRecord/Calculations/pluck
Daniel Rikowski
34

Se você também deseja selecionar campos extras:

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

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

Isso tem as vantagens de não usar seqüências de caracteres sql e não instanciar modelos

Cameron Martin
fonte
3
Isso gera um erro com o Rails 5.1 / AR 5.1 => método indefinido `uniq ''
Graham Slick
24
Model.select(:rating).uniq

Este código funciona como 'DISTINCT' (não como Array # uniq) desde os trilhos 3.2

kuboon
fonte
5
Model.select(:rating).distinct
hassan_i
fonte
2
Esta é a única resposta oficialmente correta que também é super eficiente. No entanto, adicionar .pluck(:rating)no final fará exatamente o que o OP solicitou.
Sheharyar
5

Se estou indo direto ao ponto, então:

Consulta atual

Model.select(:rating)

está retornando matriz de objeto e você escreveu uma consulta

Model.select(:rating).uniq

O uniq é aplicado na matriz de objetos e cada objeto possui um ID exclusivo. O uniq está executando seu trabalho corretamente porque cada objeto na matriz é uniq.

Existem várias maneiras de selecionar classificações distintas:

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

Mais uma coisa, primeira e segunda consulta: encontre dados distintos por consulta SQL.

Essas consultas serão consideradas "london" e "london" da mesma forma que serão negligenciadas no espaço; é por isso que ele selecionará 'london' uma vez no resultado da consulta.

Terceira e quarta consulta:

encontre dados por consulta SQL e para dados distintos aplicados ruby ​​uniq mehtod. essas consultas serão consideradas "london" e "london" diferentes; é por isso que ela selecionará 'london' e 'london', ambas no resultado da consulta.

por favor, prefira a imagem anexada para obter mais compreensão e consulte "Tourou / Aguardando RFP".

insira a descrição da imagem aqui

uma
fonte
6
map& collectsão aliases para o mesmo método, não há necessidade de fornecer exemplos para ambos.
precisa saber é o seguinte
4

Algumas respostas não levam em consideração que o OP deseja uma matriz de valores

Outras respostas não funcionam bem se o seu modelo tiver milhares de registros

Dito isto, acho que uma boa resposta é:

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

Como, primeiro, você gera uma matriz de Modelo (com tamanho diminuído por causa da seleção) e extrai o único atributo que esses modelos selecionados têm (classificações)

Fernando Fabreti
fonte
3

Se alguém está procurando o mesmo com o Mongoid, isso é

Model.distinct(:rating)
Vassilis
fonte
este não funciona agora, agora retorna múltiplos.
EUPHORAY
não retorna distinto
dowi
2

Outra maneira de coletar colunas uniq com sql:

Model.group(:rating).pluck(:rating)
Slava Zharkov
fonte