我在项目模型上有以下范围,以查找至少已完成80个众筹承诺的项目或已达到目标的项目:
class Project < ActiveRecord::Base
scope :popular, -> { collecting.where("80 <= (?) OR goal <= (?)",
Pledge.select('COUNT(*)').where("project_id = projects.id").where(paid: true),
Pledge.select('SUM(amount)').where("project_id = projects.id").where(paid: true))
}
(...)
end
这很好用,并为Postres生成以下SQL:
Project.popular
# SELECT "projects".* FROM "projects" WHERE "projects"."state" = 'collecting' AND (80 <= (SELECT COUNT(*) FROM "pledges" WHERE (project_id = projects.id) AND "pledges"."paid" = 't') OR goal <= (SELECT SUM(amount) FROM "pledges" WHERE (project_id = projects.id) AND "pledges"."paid" = 't'))
获得计数也很重要:
Project.popular.count
# SELECT COUNT(*) FROM "projects" WHERE "projects"."state" = 'collecting' AND (80 <= (SELECT COUNT(*) FROM "pledges" WHERE (project_id = projects.id) AND "pledges"."paid" = 't') OR goal <= (SELECT SUM(amount) FROM "pledges" WHERE (project_id = projects.id) AND "pledges"."paid" = 't'))
好的,子选择很好,但我觉得有一种更好,更有效的方法。我已尝试joins
和sum
,但使用聚合函数的强制group
会中断Project.popular.count
。
任何想法如何重构这个?也许只是一种用Hash表示法做where("project_id = projects.id")
的方法?
答案 0 :(得分:1)
我不知道这对你有什么好处,因为我无法在ActiveRecord术语中表达以下查询,但如果可以的话,它将比目前正在生成的糟糕查询有很大改进
select "projects".*, project_count, project_amount
from
"projects"
inner join (
select
id,
count(*) as project_count,
sum(amount) as project_amount
from pledges
group by id
where paid
) pledges using (id)
where
"projects"."state" = 'collecting'
and
(project_count >= 80 or project_amount >= goal)
答案 1 :(得分:1)
谢谢,@ Clodonaldo!通过一些小小的触摸,您的查询就像一个魅力:
select "projects".*, pledges_count, pledges_sum
from
"projects"
inner join (
select
project_id as id,
count(*) as pledges_count,
sum(amount) as pledges_sum
from pledges
where paid
group by project_id
) pledges using (id)
where
"projects"."state" = 'collecting'
and
(pledges_count >= 80 or pledges_sum >= goal)
不确定AR或AREL是否可以构建它。
更新:
我能想到的最短的是(我不需要计数和总和值):
Project.collecting.joins(
"INNER JOIN (",
Pledge.paid.group(:project_id).select("
project_id AS id,
COUNT(*) AS count,
SUM(amount) AS sum
").to_sql,
") pledges USING (id)"
).where("count >= 80 OR sum >= goal")
以上产生:
SELECT "projects".*
FROM
"projects"
INNER JOIN (
SELECT
project_id AS id,
COUNT(*) AS count,
SUM(amount) AS sum
FROM "pledges"
WHERE "pledges"."paid" = 't'
GROUP BY project_id
) pledges USING (id)
WHERE
"projects"."state" = 'collecting'
AND
(count >= 80 OR sum >= goal)