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


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

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.


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")

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

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


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


5
Model.select(:rating).distinct

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


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)


3

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

Model.distinct(:rating)

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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.