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.