2011-05-05 28 views
20

Disponen de una tabla User, UserBigge y Badge. Están conectados a través de has_many through. Un usuario puede tener varios de la misma insignia, pero quiero consultar una lista única.Rails 3 DISTINCT QUERY

@user.badges.select("DISTINCT id").order("created_at DESC") 

Se está lanzando un error:

SQLite3::SQLException: near "DISTINCT": syntax error: 

¿Cuál sería la sintaxis correcta?

EDIT: AÑADIDO ERROR TODO

SQLite3::SQLException: near "DISTINCT": syntax error: SELECT "badges".*, DISTINCT badges.id FROM "badges" INNER JOIN "userbadges" ON "badges".id = "userbadges".badges_id WHERE (("userbadges".user_id = 1)) 

Podría ser la coma entre Seleccionar y distinta?

+0

debe incluir todo el texto de error en su pregunta, hay algo extraño pasando allí. – klochner

+0

¿cuál es el SQL generado? – Vadim

+1

La coma entre el seleccionado y distinto es el problema. Estoy teniendo el mismo problema cuando pruebo una selección distinta con una combinación. Creo que el problema es que Rails agrega la primera cláusula de selección siempre que tenga un join para que solo obtenga los campos correctos, luego su llamada 'select' agrega otro y estos se concatenan con comas. Me parece un error en Rails. – David

Respuesta

3

Prueba este

@user.badges.select("DISTINCT(badges.id)").order("badges.created_at DESC") 
+0

No, todavía arroja el error – John

21

podría intentar grupo utilizando

@user.badges.group(:id) 
+0

Gracias. Funcionó perfectamente para mi caso de uso (que es básicamente el mismo que el de OP) – mindeavor

+6

Whoops nevermind. Esto no funciona con PostgreSQL. – mindeavor

+0

Sí ... no hay alegría aquí tampoco ... – baash05

27

distintas necesidades para ser inmediatamente después de la instrucción SELECT es decir

SELECT DISTINCT(badges.id), "badges".* FROM "badges" INNER JOIN "userbadges" ON "badges".id = "userbadges".badges_id WHERE (("userbadges".user_id = 1)) 

Probar:

@user.select("DISTINCT(badges.id), badges.*").badges.order("badges.created_at DESC") 

PostgreSQL necesario tener una declaración para que el campo distinto:

@user.select("DISTINCT(badges.id), badges.*").badges.order("badges.id").order("badges.created_at DESC") 
+3

Gracias por el método PostreSQL de hacer esto. :) –

+0

La parte más importante aquí es la forma de agregar insignias. * O básicamente * a cualquier consulta de selección donde desee ordenar los elementos más adelante. –

7

Conjunto

:uniq => true 

en la asociación. O crea una asociación separada solo para las insignias únicas de un usuario.

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many

Algo así como

Class User < ActiveRecord::Base 
    has_many :user_badges 
    has_many :badges, :through => :user_badges 
    has_many :unique_badges, :through => :user_badges, :source => :badges, :uniq => true 
    #untested 
end 
+2

¿Por qué esto no tiene más votos ascendentes?Todas las otras respuestas parecen estar retrógradas y complicadas, y esto funciona bien. – Armand

+1

@Alison, crear otra asociación es muy pesada y una mala solución. – sidney

+0

@sidney creando otra asociación no es * realmente * tan pesado y yo diría que no es una mala solución. Si esto tiene sentido para su caso de uso, creo que es un enfoque válido. –

3

tuve un problema similar, usando PostgreSQL también.

Mi código se veía así:

User.find(2).devices.group('token') 

que causó un error:

Device Load (0.8ms) SELECT "devices".* FROM "devices" WHERE "devices"."user_id" = 2 GROUP BY token 
ActiveRecord::StatementInvalid: PG::Error: ERROR: column "devices.id" must appear in the GROUP BY clause or be used in an aggregate function 

Solución:

Cambiar esa línea a:

User.find(2).devices.select('distinct token') 

funcionó a la perfección:

Device Load (1.3ms) SELECT distinct token FROM "devices" WHERE "devices"."user_id" = 2 
=> [#<Device token: "example">, #<Device token: "example1">, #<Device token: "example2">, #<Device token: "example3">, #<Device token: "example4">, #<Device token: "example4">] 

Actualización:

Lo sentimos, no he leído la pregunta original a fondo y en realidad han respondido a otro puesto en el hilo, quejándose del método #group no funciona con PostgreSQL. Sin embargo, sigue siendo una respuesta valiosa, así que lo dejo.

1
@user.badges.select("id").order("created_at DESC").uniq 

Éste trabaja para mí.

+0

¿Realmente necesitas la llamada a .uniq? ¿No sería eso redundante? –

+0

oh, sí lo siento, es solo .uniq –

+2

¡Considera explicar lo que hace tu código! – Victor

1

Existen al menos tres maneras de conseguir una lista única de placas por parte del usuario:

  1. @user.badges.group(:id)
  2. @user.badges.select("DISTINCT badges.*")
  3. o añadir :uniq => true en la línea

Por supuesto has_many :badges, :through , también puede encadenar en su order("badges.created_at DESC") a la consulta.