2011-07-26 25 views
10

Đã đến lúc Rails 'trừu tượng hóa từ SQL thô khiến tôi bị dơi. Trong MySQL, tôi có thể thực hiện điều này:Thực hiện update_all với các kết nối trong Rails

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 

Tôi có thể làm điều này trong Rails 3.0.1 với tải mong muốn như thế nào? Tôi đã thử tất cả những điều sau đây:

Tasks.joins(:project).where('projects.organization_id' => 42, :invoice_id => nil).update_all(:invoice_id => 7) 

Và tất cả các biến thể ở trên. Tất cả đều đưa ra lỗi hoặc không tìm thấy bất cứ điều gì.

Sau đó, tôi cố gắng sử dụng 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 

Cái này đã cho tôi lỗi undefined method 'to_sym' for #<Hash:0x1065c6438>.

Tôi đã dành quá nhiều giờ cho việc này, chỉ để nhân rộng truy vấn SQL đơn giản. Hãy giúp tôi!


EDIT: giải pháp xấu tạm thời để có được xung quanh 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(',')})"] 

Trả lời

3

"UPDATE TỪ" không phải là tiêu chuẩn SQL, do đó, nó không đáng ngạc nhiên nếu nó không được hỗ trợ trực tiếp bởi Active Record. Tuy nhiên, Active Record cung cấp cho bạn một cách để bỏ qua các trừu tượng của nó và chỉ phát hành SQL thẳng, cho những lần bạn phải làm điều gì đó mà nó không hỗ trợ. Bên trong một mô hình:

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 cũng có phương thức "select_by_sql" cho phép các câu lệnh không chuẩn của bạn trả về các mẫu bản ghi hoạt động thường xuyên.

+0

Cảm ơn, Wayne. Tôi muốn trừu tượng nếu có thể bởi vì các hương vị khác nhau của SQL xử lý loại truy vấn này khá khác nhau và tôi lo lắng về việc nó sẽ phá vỡ trong tương lai. Ví dụ của tôi ở trên có thể được thay đổi để 'cập nhật nhiệm vụ, dự án thiết lập ...' và tôi vẫn còn gặp rắc rối. Nếu bạn có thêm suy nghĩ tôi sẽ đánh giá cao nó! – glortho

+0

@Jed, Đó cũng sẽ là SQL không chuẩn. Tôi không biết Active Record sẽ trừu tượng ra sao. Một số điều bạn có thể làm với SQL không phù hợp với trừu tượng CRUD rất tốt. Điều này đặc biệt. –

+0

Thú vị. Vì vậy, 1 + n (tải không mong muốn) thực sự là cách duy nhất để làm điều này trong lớp trừu tượng? Tôi không thể tin rằng 'phạm vi' không bằng cách nào đó đưa ra một giải pháp. Chẳng phải thứ gì đó giống như nỗ lực thứ hai của tôi ở trên công việc sao? Chỉ cần đi với 1 + n cho bây giờ nhưng chắc chắn không phải là một giải pháp bền vững. Cảm ơn sự giúp đỡ của bạn ... – glortho

0

Tôi tin rằng sau

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 

có thể được viết như truy vấn Arel sau:

Tasks.include(:projects).where("projects.organization_id = ?", 42).where("tasks.invoice_id IS NULL").update_all("tasks.invoice_id = ?", 7) 

này giả định rằng bạn có sự liên kết hợp lý giữa Nhiệm vụ và dự án.

2

này được hoàn toàn ruby ​​/ ray, nó không nên được gắn thẻ như SQL -

Thông báo thông tin SQL duy nhất bạn có thể nhận được là: bắt đầu từ một cú pháp tương đương thay vì "Cập nhật từ" mà không phải là tiêu chuẩn, như ví dụ này (mà tôi sẽ không làm một trong hai nhưng hey tôi không sử dụng 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 
3

tôi tin rằng ít nhất @ Rails 3.0.8 và 2.0.10 Arel, chúng ta có thể không trực tiếp tạo ra CẬP NHẬT TỪ, nhưng nó có thể để có được kết quả tương tự với bằng cách giải quyết các tham gia như là một sub-query , ví dụ

Task.where(:invoice_id=>nil). 
where(:project_id=>Project.where(:organization_id=>42).collect(&:id)). 
update_all(:invoice_id => 7) 

này tạo SQL như:

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 

Trong ourse, đó là giải quyết các phụ truy vấn trong Rails không trong SQL.Để tạo một phụ chọn trong SQL, bạn có thể trộn trong một Arel chút như thế này:

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) 

nào tạo sql như thế này:

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 
); 

Có một Arel cách tinh khiết để làm điều này, nhưng Cú pháp UpdateManager được viết dưới dạng tài liệu

Các vấn đề liên quan