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 Uckun 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,