2013-09-23 41 views
6

Làm cách nào để cập nhật/lưu nhiều phiên bản của một mô hình trong một lần chụp, sử dụng khối giao dịch trong Rails?Cập nhật nhiều bản ghi trong một giao dịch ActiveRecord trong Rails

Tôi muốn cập nhật giá trị cho hàng trăm bản ghi; các giá trị khác nhau cho mỗi bản ghi. Đây là không phải tình huống cập nhật hàng loạt cho một thuộc tính. Model.update_all (attr: value) không thích hợp ở đây.

MyModel.transaction do 
    things_to_update.each do |thing| 
     thing.score = rand(100) + rand(100) 
     thing.save 
    end 
end 

save dường như phát hành giao dịch của chính nó, thay vì thực hiện các cập nhật cho giao dịch xung quanh. Tôi muốn tất cả các bản cập nhật được thực hiện trong một giao dịch lớn.

Tôi làm cách nào để thực hiện việc này?

+1

có thể là activerecord-import [gem] (https://github.com/zdennis/activerecord-import/wiki) sẽ làm việc cho bạn .. – tihom

Trả lời

0

Tôi không chắc chắn, nhưng có thể bạn đang gây nhầm lẫn nhiều giao dịch với nhiều truy vấn.

Mã bạn đã đăng sẽ tạo một giao dịch duy nhất (ví dụ: nếu xảy ra ngoại lệ thì tất cả các bản cập nhật sẽ được khôi phục), nhưng mỗi save sẽ dẫn đến truy vấn cập nhật riêng.

Nếu có thể thực hiện cập nhật bằng cách sử dụng SQL thay vì mã Ruby thì đó có thể là cách tốt nhất để thực hiện.

+0

Tôi phải chạy các bản cập nhật trên 1000 hoặc nhiều bản ghi tại một thời điểm, vì vậy tôi không muốn chạy 1000 truy vấn cập nhật riêng biệt, vì nó chậm hơn nhiều. Bạn có thể thực hiện tất cả chúng trong một shot không? – Avishai

+0

Nếu bản cập nhật thực sự là đặt 'điểm số' thành tổng của 2 số ngẫu nhiên (và bạn không chỉ sử dụng làm ví dụ để đơn giản hóa câu hỏi) thì bạn có thể sử dụng' update_all' nhưng đặt giá trị bằng cách sử dụng postgres ' chức năng ngẫu nhiên thay vì sử dụng các giá trị cố định. – mikej

3

Giả sử bạn biết rằng bạn muốn thiết lập những điều với id 1, 2, và 3 để có điểm, 2, 8, và 64 (như trái ngược với chỉ số ngẫu nhiên), bạn có thể:

UPDATE 
    things AS t 
SET 
    score = c.score 
FROM 
    (values 
    (1, 2), 
    (2, 30), 
    (4, 50) 
) as c(id, score) 
where c.id = t.id; 

Vì vậy, với Rails, bạn nên sử dụng ActiveRecord::Base.connection#execute để thực hiện một khối tương tự như ở trên, nhưng với chuỗi giá trị đúng được nội suy.

-1

Tôi nghĩ rằng bạn chỉ cần thay đổi phương thức "lưu" thành "lưu!". Nếu bất kỳ bản cập nhật nào không thành công, phương pháp sẽ lưu! tạo ra một ngoại lệ. Khi ngoại lệ xảy ra trong khối giao dịch, giao dịch sẽ đảo ngược toàn bộ hoạt động (rollback)

MyModel.transaction do 
    things_to_update.each do |thing| 
    thing.score = rand(100) + rand(100) 
    thing.save! 
end 
+0

Điều này sẽ tạo ra nhiều yêu cầu SQL. –

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