Making destroy_all work faster

When I call destoy_all on a model with a where clause AR iterates
though each record in the recordset and calls destroy on each item.
This seems highly inefficient to me. Wouldn’t it be better to call
delete from child records where foreign_id in (parent ids)?

In other words shouldn’t destroy all destroy all the children for all
the items in one shot?

I am having to manually do destroy all on my children when I have
large number of records to delete so I am wondering if there is a more
elegant way of handling this.

Thanks.

When I call destoy_all on a model with a where clause AR iterates
though each record in the recordset and calls destroy on each item.
This seems highly inefficient to me. Wouldn’t it be better to call
delete from child records where foreign_id in (parent ids)?

In other words shouldn’t destroy all destroy all the children for all
the items in one shot?

No. Destroy has to instantiate each object prior to actually removing
it from the database in order to run any before/after destroy call
backs.

You can speed things up if you don’t need this by tweaking the
:dependent option to has_many so that it will simply use SQL’s DELETE on
the child objects… but that of course won’t run any callbacks (your
own, or Rail’s counter cache, etc.)

No. Destroy has to instantiate each object prior to actually removing it from
the database in order to run any before/after destroy call backs.

You can speed things up if you don’t need this by tweaking the :dependent option
to has_many so that it will simply use SQL’s DELETE on the child objects… but
that of course won’t run any callbacks (your own, or Rail’s counter cache, etc.)

Actually it doesn’t HAVE to instantiate them if it can query the
relationships in a meaningful matter. Of course we know that it CAN
query those relationships and calculate what has to be deleted.

I did this…

id_list = Model.select(:id).where(whereclause)…map { |r| r.id
}.join(’,’)

This gives you a list of comma delimited ids

ActiveRecord::Base.transaction do

Children.delete_all('parent_id in (#{id_list})")

Children2.delete_all('parent_id in (#{id_list})")
Children3.delete_all('parent_id in (#{id_list})")
end

In my case I had about a half a dozen children.

The delete time for my records went from four minutes to under 30
seconds. That’s a pretty big gain. Of course I had to hand code my
relationships and if I add something new I will have to modify my
method. It would be really nice if somebody way smarter than me
implemented this into AR.

Tim U. wrote in post #976165:

No. Destroy has to instantiate each object prior to actually removing it from
the database in order to run any before/after destroy call backs.

You can speed things up if you don’t need this by tweaking the :dependent
option
to has_many so that it will simply use SQL’s DELETE on the child
objects… but
that of course won’t run any callbacks (your own, or Rail’s counter
cache, etc.)

Actually it doesn’t HAVE to instantiate them if it can query the
relationships in a meaningful matter. Of course we know that it CAN
query those relationships and calculate what has to be deleted.

Unfortunately, that’s not always all that a before_destroy callback
does. Since that’s the case, there’s no general method for executing
the callbacks without instantiating all those objects.

Of course, if you know you don’t need the callbacks, it’s perfectly fine
to use delete_all instead.

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Sent from my iPhone