Rieles: seleccione valores únicos de una columna


238

Ya tengo una solución que funciona, pero realmente me gustaría saber por qué esto no funciona:

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

Selecciona, pero no imprime valores únicos, imprime todos los valores, incluidos los duplicados. Y está en la documentación: http://guides.rubyonrails.org/active_record_querying.html#selecting-specific-fields


Respuestas:


449
Model.select(:rating)

El resultado de esto es una colección de Modelobjetos. No clasificaciones simples. Y desde uniqel punto de vista, son completamente diferentes. Puedes usar esto:

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

o esto (más eficiente)

Model.uniq.pluck(:rating)

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

Actualizar

Aparentemente, a partir de rails 5.0.0.1, solo funciona en consultas de "nivel superior", como las anteriores. No funciona en proxies de colección (relaciones "has_many", por ejemplo).

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

En este caso, deduplicar después de la consulta

user.addresses.pluck(:city).uniq # => ['Moscow']

Hice un: group (: rating) .collect {| r | r.rating} Dado que map == collect, ¿dónde puedo leer sobre esta sintaxis que utilizó (&: rating)? No veo esto en la documentación de Ruby.
alexandrecosta

@ user1261084: vea Symbol # to_proc para comprender .map (&: rating). PragDave explica
dbenhur

63
Vale la pena señalar que Model.uniq.pluck(:rating)es la forma más eficiente de hacer esto: esto genera SQL que usa en SELECT DISTINCTlugar de aplicar .uniqa una matriz
Mikey

23
En Rails 5, Model.uniq.pluck(:rating)seráModel.distinct.pluck(:rating)
neurodinámico

2
Si desea seleccionar valores únicos de la relación has_many, siempre puede hacerloModel.related_records.group(:some_column).pluck(:some_column)
Krzysztof Karski

92

Si va a usar Model.select, entonces también podría usar DISTINCT, ya que devolverá solo los valores únicos. Esto es mejor porque significa que devuelve menos filas y debería ser un poco más rápido que devolver varias filas y luego decirle a Rails que elija los valores únicos.

Model.select('DISTINCT rating')

Por supuesto, esto se proporciona si su base de datos comprende la DISTINCTpalabra clave, y la mayoría debería.


66
Model.select("DISTINCT rating").map(&:rating)para obtener una variedad de solo las calificaciones.
Kris

Ideal para aquellos con aplicaciones heredadas que usan Rails 2.3
Mikey

3
Sí, esto funciona fantástico, sin embargo, pero solo devuelve el atributo DISTINCT. ¿Cómo puede devolver todo el objeto Modelo siempre que sea distinto? Para que tenga acceso a todos los atributos en el modelo en instancias donde el atributo es único.
zero_cool

@Jackson_Sandland Si desea un objeto Modelo, deberá crear una instancia de un registro en la tabla. Pero no está seleccionando un registro solo un valor único (de lo que puede ser múltiples registros).
Benissimo


34

Si también desea seleccionar campos adicionales:

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"

Esto tiene las ventajas de no usar cadenas sql y no crear modelos de instancia


3
Esto arroja un error con Rails 5.1 / AR 5.1 => método indefinido `uniq '
Graham Slick

24
Model.select(:rating).uniq

Este código funciona como 'DISTINCT' (no como Array # uniq) ya que rails 3.2


5
Model.select(:rating).distinct

2
Esta es la única respuesta oficialmente correcta que también es súper eficiente. Sin .pluck(:rating)embargo , agregar al final hará que sea exactamente lo que solicitó el OP.
Sheharyar

5

Si voy directo al camino, entonces:

Consulta actual

Model.select(:rating)

está devolviendo una matriz de objetos y ha escrito consulta

Model.select(:rating).uniq

uniq se aplica en una matriz de objetos y cada objeto tiene una identificación única. uniq está realizando su trabajo correctamente porque cada objeto en la matriz es uniq.

Hay muchas formas de seleccionar una calificación distinta:

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

o

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

o

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

o

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

Una cosa más, primera y segunda consulta: encuentre datos distintos por consulta SQL.

Estas consultas se considerarán "londres" y "londres" lo mismo significa que descuidará el espacio, es por eso que seleccionará 'londres' una vez en el resultado de su consulta.

Tercera y cuarta consulta:

buscar datos por consulta SQL y para datos distintos aplicados ruby ​​uniq mehtod. estas consultas se considerarán "londres" y "londres" diferentes, por eso seleccionará 'londres' y 'londres' en el resultado de su consulta.

por favor, prefiera la imagen adjunta para una mejor comprensión y eche un vistazo a "Recorrido / En espera de RFP".

ingrese la descripción de la imagen aquí


66
map& collectson alias para el mismo método, no hay necesidad de proporcionar ejemplos para ambos.
Adam Lassek

4

Algunas respuestas no tienen en cuenta que el OP quiere una matriz de valores

Otras respuestas no funcionan bien si su modelo tiene miles de registros

Dicho esto, creo que una buena respuesta es:

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

Porque, primero genera una matriz de Modelo (con un tamaño disminuido debido a la selección), luego extrae el único atributo que tienen esos modelos seleccionados (calificaciones)


3

Si alguien está buscando lo mismo con Mongoid, eso es

Model.distinct(:rating)

este no funciona ahora, ahora devuelve múltiplos.
EUPHORAY

no vuelve distinta
dowi

2

Otra forma de recopilar columnas uniq con sql:

Model.group(:rating).pluck(:rating)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.