我有一个 Rails 应用程序,它使用 postgresql 作为数据库,按位置对不同类型的用户进行排序,然后按他们在网站上的各种活动收到的信誉点。这是一个示例查询
@lawyersbylocation = User.lawyers_by_province(province).sort_by{ |u| -u.total_votes }
该查询调用 User.rb 模型上的作用域律师_by_province:
scope :lawyers_by_province, lambda {|province|
joins(:contact).
where( contacts: {province_id: province},
users: {lawyer: true})
}
然后,仍然在 User.rb 模型上,它计算他们拥有的声誉点。
def total_votes
answerkarma = AnswerVote.joins(:answer).where(answers: {user_id: self.id}).sum('value')
contributionkarma = Contribution.where(user_id: self.id).sum('value')
bestanswer = BestAnswer.joins(:answer).where(answers: {user_id: self.id}).sum('value')
answerkarma + contributionkarma + bestanswer
end
有人告诉我,如果站点达到一定数量的用户,那么它会变得非常慢,因为它是在 Ruby 中而不是在数据库级别进行排序的。我知道评论指的是total_votes 方法,但我不确定lawers_by_province 是在数据库级别还是在ruby 中发生,因为它使用Rails 助手来查询数据库。对我来说似乎是两者的混合,但我不确定这对效率的影响。
你能告诉我如何写这个,以便查询在数据库级别发生,因此以更有效的方式不会破坏我的网站吗?
更新以下是 total_votes 方法中模型的三种方案。
create_table "answer_votes", force: true do |t|
t.integer "answer_id"
t.integer "user_id"
t.integer "value"
t.boolean "lawyervote"
t.boolean "studentvote"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "answer_votes", ["answer_id"], name: "index_answer_votes_on_answer_id", using: :btree
add_index "answer_votes", ["lawyervote"], name: "index_answer_votes_on_lawyervote", using: :btree
add_index "answer_votes", ["studentvote"], name: "index_answer_votes_on_studentvote", using: :btree
add_index "answer_votes", ["user_id"], name: "index_answer_votes_on_user_id", using: :btree
create_table "best_answers", force: true do |t|
t.integer "answer_id"
t.integer "user_id"
t.integer "value"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "question_id"
end
add_index "best_answers", ["answer_id"], name: "index_best_answers_on_answer_id", using: :btree
add_index "best_answers", ["user_id"], name: "index_best_answers_on_user_id", using: :btree
create_table "contributions", force: true do |t|
t.integer "user_id"
t.integer "answer_id"
t.integer "value"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "contributions", ["answer_id"], name: "index_contributions_on_answer_id", using: :btree
add_index "contributions", ["user_id"], name: "index_contributions_on_user_id", using: :btree
此外,这里是包含在 user.rb 模型上的律师_by_province 范围中使用的province_id 的联系方案
create_table "contacts", force: true do |t|
t.string "firm"
t.string "address"
t.integer "province_id"
t.string "city"
t.string "postalcode"
t.string "mobile"
t.string "office"
t.integer "user_id"
t.string "website"
t.datetime "created_at"
t.datetime "updated_at"
end
更新试图应用@Shawn的答案,我把这个方法放在user.rb模型中
def self.total_vote_sql
"( " +
[
AnswerVote.joins(:answer).select("user_id, value"),
Contribution.select("user_id, value"),
BestAnswer.joins(:answer).select("user_id, value")
].map(&:to_sql) * " UNION ALL " +
") as total_votes "
end
然后在控制器中,我做了这个(放在User
前面total_vote_sql
)
@lawyersbyprovince = User.select("users.*, sum(total_votes.value) as total_votes").joins("left outer join #{User.total_vote_sql} on users.id = total_votes.user_id").
order("total_votes desc").lawyers_by_province(province)
它给了我这个错误
ActiveRecord::StatementInvalid in LawyerProfilesController#index
PG::Error: ERROR: column reference "user_id" is ambiguous LINE 1: ..."user_id" = "users"."id" left outer join ( SELECT user_id, v... ^ : SELECT users.*, sum(total_votes.value) as total_votes FROM "users" INNER JOIN "contacts" ON "contacts"."user_id" = "users"."id" left outer join ( SELECT user_id, value FROM "answer_votes" INNER JOIN "answers" ON "answers"."id" = "answer_votes"."answer_id" UNION ALL SELECT user_id, value FROM "contributions" UNION ALL SELECT user_id, value FROM "best_answers" INNER JOIN "answers" ON "answers"."id" = "best_answers"."answer_id") as total_votes on users.id = total_votes.user_id WHERE "contacts"."province_id" = 6 AND "users"."lawyer" = 't' ORDER BY total_votes desc
更新对 Shawn 的帖子应用编辑后,错误消息现在是这样的:
PG::Error: ERROR: column reference "user_id" is ambiguous LINE 1: ..."user_id" = "users"."id" left outer join ( SELECT user_id as... ^ : SELECT users.*, sum(total_votes.value) as total_votes FROM "users" INNER JOIN "contacts" ON "contacts"."user_id" = "users"."id" left outer join ( SELECT user_id as tv_user_id, value FROM "answer_votes" INNER JOIN "answers" ON "answers"."id" = "answer_votes"."answer_id" UNION ALL SELECT user_id as tv_user_id, value FROM "contributions" UNION ALL SELECT user_id as tv_user_id, value FROM "best_answers" INNER JOIN "answers" ON "answers"."id" = "best_answers"."answer_id") as total_votes on users.id = total_votes.tv_user_id WHERE "contacts"."province_id" = 6 AND "users"."lawyer" = 't' ORDER BY total_votes desc