1
\$\begingroup\$

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?

\$\endgroup\$
  • \$\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 Aug 1 '13 at 2:08
2
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.