簡體   English   中英

在Rails中使用連接執行update_all

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM