I've got relation in my database as following :
Customer has many Orders
Order has many Parts
Order has many Upc
So my requirement is to copy all orders from one customer and attach
or copy those orders (with their dependencies including parts and upcs) to another customer. With originals customers orders to be left in-tacked, so I'd basically clone
orders and dependencies from customer a
to customer b
Here is what I got so far, but its kinda slow (slowest first):
I'd iterate trough customers orders first and then I'd call copy_order1
method to copy order and dependencies to another customer. here is copy_order1
(which is in class Customer):
def copy_order1(source_order)
order_attributes = source_order.attributes.merge({:customer_id => self.customer_id})
['id', 'deleted_at'].each{|k| order_attributes.delete k}
ord = Order.create(order_attributes)
source_order.parts.each do |part|
part_attributes = part.attributes.merge({:order_id => ord.id})
['id', 'deleted_at'].each{|k| part_attributes.delete k}
Part.create(part_attributes)
end
source_order.upcs.each do |upc|
upc_attributes = upc.attributes.merge({:order_id => ord.id})
['id', 'deleted_at'].each{|k| upc_attributes.delete k}
Upc.create(upc_attributes)
end
return ord
end
And there I made slightly faster method called copy_order2
:
def copy2(destination_customer)
ActiveRecord::Base.transaction do
Order.includes(:customer, :part, :upc).where(:customer_id => self.id).find_in_batches(:batch_size => 50) do |batch|
batch.each do |order|
new_order = order.dup
new_order.parts << order.parts.each {|part| tmp = part.dup; tmp.id = tmp.order_id = nil; tmp}
new_order.upcs << order.upcs.each {|upcs| tmp = upc.dup; tmp.id = tmp.order_id = nil; tmp}
destination_customer.orders << new_ordert
end
# Save one batch
destination_customer.save!
end
end
end
Can anyone suggest performance friendly way
to do this from your own experience? How would you approach this issue if you had one?
-
\$\begingroup\$ Wouldn't it be better to simply point the second customer to the same order, rather than introducing duplication and a deep copy? Or do you want the orders to be updated independently after the copy? \$\endgroup\$Mark Thomas– Mark Thomas2013年08月01日 02:08:28 +00:00Commented Aug 1, 2013 at 2:08
1 Answer 1
The #dups are probably unnecessary. If you unset the ID ActiveRecord already "thinks" that the object is new, and #save will create a new one in the DB. But the main bottleneck is probably the amount of DB queries generated. The easiest way to gain some performance in such cases is to wrap the whole block into a transaction. The best way would probably use bulk inserts (e.g. with a gem like https://github.com/zdennis/activerecord-import).
But if you've to do so many deep copies you also should rethink if you're using the right approach (or DB) here.