Codifica el código amistoso ActiveRecord de una consulta de Integración compleja, Suma y Grupo

PROBLEMA

Hola,

No estoy teniendo suerte tratando de descomponer esta statement SQL en código amigable de ActiveRecord / Rails y me gustaría aprender cómo puedo evitar una instrucción find_by_sql en esta situación.

Escenario I tienen usuarios que crean auditorías cuando realizan una acción. Cada auditoría es de una audit_activity específica. Cada audit_activity vale un cierto número de puntos, en function de score_weight. Necesito encontrar las puntuaciones totales de cada usuario, en function de su total acumulado audit_activity score_weights. Eventualmente tendré que clasificarlos, lo que significa agregar una sorting a esto también.

Mi código Aquí está mi sql y versiones simplificadas de las tablas en cuestión. ¿Alguna idea?

SQL con nombres de columna completos (para mayor claridad)

SELECT users.id, u.email, SUM(audit_activity.score_weight) FROM users JOIN audits ON users.id = audits.user_id JOIN audit_activities ON audit_activities.id = audits.audit_activity_id GROUP BY users.id; 

Modelos: Usuario, Auditoría, AuditActivity

Campos de usuario: id, correo electrónico

 class User < ActiveRecord::Base include Clearance::User has_many :audits end 

Campos de auditoría: id, user_id, audit_activity_id

 class Audit < ActiveRecord::Base belongs_to :user belongs_to :audit_activity end 

Campos de AuditActivity: id, score_weight

 class AuditActivity < ActiveRecord::Base has_many :audits end 

Ejemplo de datos

Aquí hay un set de sentencias de SQL para que pueda jugar con datos similares con los que estoy trabajando y ver qué surge cuando se ejecuta la consulta en cuestión. Debería poder copyr / pegar todo en un browser de consulta de database.

 CREATE TABLE users( id INTEGER NOT NULL, email TEXT (25), PRIMARY KEY (id) ); CREATE TABLE audits( id INTEGER NOT NULL, user_id INTEGER, audit_activity_id INTEGER, PRIMARY KEY (id) ); CREATE TABLE audit_activities( id INTEGER NOT NULL, score_weight INTEGER, PRIMARY KEY (id) ); INSERT INTO users(id, email) VALUES(1, "[email protected]"); INSERT INTO users(id, email) VALUES(2, "[email protected]"); INSERT INTO users(id, email) VALUES(3, "[email protected]"); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(1, 1, 1); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(2, 1, 2); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(3, 1, 1); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(4, 1, 3); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(5, 1, 1); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(6, 1, 4); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(7, 2, 4); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(8, 2, 4); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(9, 2, 4); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(10, 3, 3); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(11, 3, 2); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(12, 3, 2); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(13, 3, 2); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(14, 3, 3); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(15, 3, 1); INSERT INTO audits(id, user_id, audit_activity_id) VALUES(16, 3, 1); INSERT INTO audit_activities(id, score_weight) VALUES(1, 1); INSERT INTO audit_activities(id, score_weight) VALUES(2, 2); INSERT INTO audit_activities(id, score_weight) VALUES(3, 7); INSERT INTO audit_activities(id, score_weight) VALUES(4, 11); 

The Query Again, aquí está la consulta.

 SELECT u.id, u.email, SUM(aa.score_weight) FROM users u JOIN audits a ON u.id = a.user_id JOIN audit_activities aa ON aa.id = a.audit_activity_id GROUP BY u.id; 

 User.sum( :score_weight, :include => {:audits => :audit_activity}, :group => 'users.id' ) 

Es bastante fácil get sus usuarios, y repetir las auditorías para cada uno de ellos, resumiendo los valores sobre la marcha. Entonces sería algo como esto:

 users = User.find (: todos)
 usuarios. cada do | usuario |
   pone "user: # {user.email}"
   puntaje = 0
   user.audits.each do | audit |
        pone "audit: # {audit.audit_activity.id} puntaje: # {audit.audit_activity.score_weight}"
        puntuación + = audit.audit_activity.score_weight
   fin
   pone "puntaje total para este usuario: # {puntaje"
 fin

Eso generará muchas consultas separadas, sin embargo, eso no siempre es malo.

Si los volúmenes de datos van a ser grandes, y como dices, querrás clasificar por puntaje de usuario, entonces creo que la respuesta será tener un campo con el puntaje actual en el logging del usuario, que se actualiza cada vez que logging de actividad de auditoría está escrito. Esto se puede hacer automáticamente con una callback de asociación (es decir, un método after_add en la asociación de auditoría en el logging de usuario). Ver http://guides.rubyonrails.org/association_basics.html#association-callbacks .