从复杂的Join,Sum和Group查询中调用ActiveRecord友好代码

问题

你好,

我没有运气试图将这个SQL语句分解为ActiveRecord / Rails友好代码,我想学习如何在这种情况下避免使用find_by_sql语句。

场景我有用户在执行操作时创建审核。 每次审核都是特定的audit_activity。 根据score_weight,每个audit_activity值得一定数量的积分。 我需要根据他们累计的audit_activity score_weights找到每个用户的总分。 最终我需要对它们进行排名,这意味着也要对它进行排序。

我的代码这是我的sql和有问题的表的简化版本。 有什么想法吗?

带有完整列名的SQL(为清楚起见)

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; 

模型:用户,审计,AuditActivity

用户字段:id,email

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

审计字段:id,user_id,audit_activity_id

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

AuditActivity字段:id,score_weight

 class AuditActivity < ActiveRecord::Base has_many :audits end 

示例数据

这是一组SQL语句,因此您可以使用我正在使用的类似数据,并查看运行相关查询时出现的情况。 您应该能够将整个事物复制/粘贴到数据库查询浏览器中。

 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, "1user@a.com"); INSERT INTO users(id, email) VALUES(2, "2user@b.com"); INSERT INTO users(id, email) VALUES(3, "3user@c.com"); 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); 

再次查询 ,这是查询。

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

这很容易让您的用户,并迭代他们每个人的审计,总结你的价值观。 所以它会是这样的:

 users = User.find(:all)
 users.each do | user |
  把“user:#{user.email}”
  得分= 0
   user.audits.each do | audit |
       把“audit:#{audit.audit_activity.id}得分:#{audit.audit_activity.score_weight}”
       得分+ = audit.audit_activity.score_weight
  结束
  把“这个用户的总得分:#{score”
结束

然而,这将产生许多单独的查询,但这并不总是坏事。

如果数据量很大,正如您所说,您将需要按用户分数排序,那么我认为答案将是在用户记录上有一个当前分数的字段,每次更新时都会更新审计活动记录已写入。 这可以通过关联回调自动完成(即用户记录上的审计关联上的after_add方法)。 请参阅http://guides.rubyonrails.org/association_basics.html#association-callbacks 。