就像这样,Rails从原始SQL中抽象出来让我感到沮丧。在MySQL中,我可以这样做:
UPDATE FROM tasks AS t
LEFT JOIN projects as p
ON t.project_id = p.id
SET t.invoice_id = 7
WHERE p.organization_id == 42
AND t.invoice_id IS NULL
如何在热门加载的Rails 3.0.1中完成此操作?我已经尝试了以下所有方法:
Tasks.joins(:project).where('projects.organization_id' => 42, :invoice_id => nil).update_all( :invoice_id => 7 )
以上的所有变化。所有人都提出错误或没有找到任何结果。
然后我尝试使用scope
:
Task.scope :find => {:joins => :project, :conditions => ["projects.organization_id == ? AND invoice_id IS NULL", @organization.id] } do
Task.update_all :invoice_id => @invoice.id
end
这个给了我错误undefined method 'to_sym' for #<Hash:0x1065c6438>
。
我花了太多时间来处理这个问题,只是为了复制一个简单的SQL查询。请帮忙!
编辑:绕过n + 1的暂时不良解决方案:
task_ids = Task.select('tasks.id').joins(:project).where('projects.organization_id' => @organization.id, :invoice_id => nil).collect{|t| t.id}
Task.update_all ['invoice_id = ?', @invoice.id], ["id in (#{task_ids.join(',')})"]
答案 0 :(得分:3)
“UPDATE FROM”不是标准SQL,因此如果Active Record不直接支持它也就不足为奇了。但是,Active Record确实为您提供了一种绕过其抽象的方法,并且只需要发出直接的SQL,那时您必须做一些它不支持的事情。在模型中:
sql = "UPDATE FROM tasks AS t
LEFT JOIN projects as p
ON t.project_id = p.id
SET t.invoice_id = 7
WHERE p.organization_id == 42
AND t.invoice_id IS NULL"
connection.update_sql(sql)
ActiveRecord :: Base还有一个“select_by_sql”方法,允许非标准select语句返回常规的活动记录模型实例。
答案 1 :(得分:3)
我至少相信@ rails 3.0.8和ARel 2.0.10,我们无法直接生成UPDATE FROM,但是可以通过将连接解析为子查询来获得相同的结果,例如
Task.where(:invoice_id=>nil).
where(:project_id=>Project.where(:organization_id=>42).collect(&:id)).
update_all(:invoice_id => 7)
这会生成SQL,如:
UPDATE "tasks"
SET "invoice_id" = 7
WHERE "invoice_id" IS NULL AND "project_id" IN (1,2,3);
-- assuming projects 1,2,3 have organization_id = 42
当然,这是在Rails中解析子查询而不是在SQL中。要在SQL中生成子选择,您可以混合使用像这样的小Arel:
t = Task.arel_table
p = Project.arel_table
Task.where(:invoice_id=>nil).
where(
t[:project_id].in(
p.where(
p[:organization_id].eq(42)
).project(p[:id])
)
).update_all(:invoice_id => 7)
哪个生成这样的SQL:
UPDATE "tasks"
SET "invoice_id" = 7
WHERE "invoice_id" IS NULL
AND "project_id" IN (
SELECT "projects"."id" FROM "projects"
WHERE "projects"."organization_id" = 42
);
有一种纯粹的ARel方式可以做到这一点,但是UpdateManager语法严重缺乏记录
答案 2 :(得分:2)
这纯粹是ruby / rails,不应该标记为SQL -
你可以获得的唯一SQL信息是:从另一个语法等价物开始而不是“更新来自”,这不是标准的,例如这个(我也不会这样做但是我不使用ruby /导轨)。
UPDATE tasks t
SET t.invoice_id=7
WHERE
t.invoice_id IS NULL
AND
(SELECT
p.organization_id
FROM tasks t2
LEFT JOIN projects p
ON t.project_id=p.id
WHERE t2.id=t.id)=42
答案 3 :(得分:0)
我相信以下
UPDATE FROM tasks AS t
LEFT JOIN projects as p
ON t.project_id = p.id
SET t.invoice_id = 7
WHERE p.organization_id == 42
AND t.invoice_id IS NULL
可以写成以下Arel查询:
Tasks.include(:projects).where("projects.organization_id = ?", 42).where("tasks.invoice_id IS NULL").update_all("tasks.invoice_id = ?", 7)
这假设您在任务和项目之间拥有正确的关联。