[英]Doing an update_all with joins in Rails
就像這樣,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>
的錯誤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(',')})"]
“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語句返回常規活動記錄模型實例。
我至少相信@ 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語法很糟糕
這純粹是ruby / rails,它不應該被標記為SQL -
你可以得到的唯一的SQL信息是:從另一個語法等價物開始而不是“更新來自”,這不是標准的,例如這個(我也不會做,但是我不使用ruby / rails)。
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
我相信以下幾點
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)
這假設您在任務和項目之間具有正確的關聯。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.