219

I am looking for the best approach to delete records from a table. For instance, I have a user whose user ID is across many tables. I want to delete this user and every record that has his ID in all tables.

u = User.find_by_name('JohnBoy')
u.usage_indexes.destroy_all
u.sources.destroy_all
u.user_stats.destroy_all
u.delete

This works and removes all references of the user from all tables, but I heard that destroy_all was very process heavy, so I tried delete_all. It only removes the user from his own user table and the id from all the other tables are made null, but leaves the records intact in them. Can someone share what the correct process is for performing a task like this?

I see that destroy_all calls the destroy function on all associated objects but I just want to confirm the correct approach.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
glogic
  • 4,042
  • 3
  • 28
  • 44

4 Answers4

285

You are right. If you want to delete the User and all associated objects -> destroy_all However, if you just want to delete the User without suppressing all associated objects -> delete_all

According to this post : Rails :dependent => :destroy VS :dependent => :delete_all

  • destroy / destroy_all: The associated objects are destroyed alongside this object by calling their destroy method
  • delete / delete_all: All associated objects are destroyed immediately without calling their :destroy method
Community
  • 1
  • 1
Sandro Munda
  • 39,921
  • 24
  • 98
  • 123
  • 89
    It should also be noted that 1) Callbacks are not called when using `delete_all`, and 2) `destroy_all` instantiates all the records and destroys them one at a time, so with a very large dataset, this could be painfully slow. – Dylan Markow Jul 14 '11 at 19:15
  • suppose i'm running a before_destroy method in the model - if i use delete_all then this method won't run? secondly if i employ a before_delete method in my model, will this run when i run delete or delete_all in the rails console? – BenKoshy Oct 12 '16 at 02:26
29

delete_all is a single SQL DELETE statement and nothing more. destroy_all calls destroy() on all matching results of :conditions (if you have one) which could be at least NUM_OF_RESULTS SQL statements.

If you have to do something drastic such as destroy_all() on large dataset, I would probably not do it from the app and handle it manually with care. If the dataset is small enough, you wouldn't hurt as much.

Ryan Her
  • 1,067
  • 1
  • 9
  • 14
17

To avoid the fact that destroy_all instantiates all the records and destroys them one at a time, you can use it directly from the model class.

So instead of :

u = User.find_by_name('JohnBoy')
u.usage_indexes.destroy_all

You can do :

u = User.find_by_name('JohnBoy')
UsageIndex.destroy_all "user_id = #{u.id}"

The result is one query to destroy all the associated records

simon-olivier
  • 3,439
  • 2
  • 14
  • 6
1

I’ve made a small gem that can alleviate the need to manually delete associated records in some circumstances.

This gem adds a new option for ActiveRecord associations:

dependent: :delete_recursively

When you destroy a record, all records that are associated using this option will be deleted recursively (i.e. across models), without instantiating any of them.

Note that, just like dependent: :delete or dependent: :delete_all, this new option does not trigger the around/before/after_destroy callbacks of the dependent records.

However, it is possible to have dependent: :destroy associations anywhere within a chain of models that are otherwise associated with dependent: :delete_recursively. The :destroy option will work normally anywhere up or down the line, instantiating and destroying all relevant records and thus also triggering their callbacks.

Community
  • 1
  • 1
jaynetics
  • 1,234
  • 12
  • 15
  • This is fantastic! I wonder why not more people have watched/starred/forked it on github.. is it still working well? – Magne May 12 '17 at 14:37
  • @Magne Thanks! It should be working. The tests run on Ruby 2.4.1 and Rails 5.1.1. So far I've only used it privately and not in major production apps, hence the major version "0", but I never noticed any issues. It's also fairly simple, so it should be fine. – jaynetics May 13 '17 at 17:50
  • Cool. :) I am running a project on Ruby 2.3.1, and 'rails', '~>4.1.14', and am sadly forced to rely on activerecord (~> 4.1.0) due to other gems. I see that delete_recursively is resolved to 0.9.0. Is there an older version of it that would work with activerecord 4.1 ? I couldn't find any in the releases tab on github. – Magne May 15 '17 at 08:07
  • 1
    @Magne I found it actually [works for activerecord as low as 4.1.14](https://travis-ci.org/janosch-x/delete_recursively/builds/232587427) and have released gem version 1.0.0 with a relaxed dependency. Keep in mind that Rails' 4.1 branch no longer receives security updates, though. – jaynetics May 15 '17 at 21:06