ActiveRecord - Get the last n records and delete them in one command?
To do it in one SQL query use delete_all
:
Model.order(created_at: :desc).limit(n).delete_all
But delete_all
won't execute any model callbacks or validations
To run callbacks and validations use destroy_all
:
Model.order(created_at: :desc).limit(n).destroy_all
Unfortunately destroy_all
will execute n + 1 SQL queries: 1 query to retrieve records and n queries to delete each record.
You can achieve this by:
Model.last(n).each(&:destroy)
@IvanDenisov points out another way to do this:
Model.order('created_at DESC').limit(n).destroy_all
It's basically doing the same thing according to Rails API Doc, but a little bit verbose. Besides, it doesn't do all things in one SQL query.
Detailed comparison of SQL queries
I tried to run both codes in rails console
under Ruby 2.0.0p253 && Rails 4.0.4, here are the results:
2.0.0p353 :002 > Role.last(3).each(&:destroy)
Role Load (1.0ms) SELECT "roles".* FROM "roles" ORDER BY "roles"."id" DESC LIMIT 3
(0.3ms) BEGIN
SQL (3.5ms) DELETE FROM "roles" WHERE "roles"."id" = $1 [["id", 5487]]
(11.8ms) COMMIT
(0.1ms) BEGIN
SQL (0.2ms) DELETE FROM "roles" WHERE "roles"."id" = $1 [["id", 5488]]
(5.4ms) COMMIT
(0.1ms) BEGIN
SQL (0.2ms) DELETE FROM "roles" WHERE "roles"."id" = $1 [["id", 5489]]
(4.6ms) COMMIT
2.0.0p353 :004 > Role.order('created_at DESC').limit(3).destroy_all
Role Load (0.9ms) SELECT "roles".* FROM "roles" ORDER BY created_at DESC LIMIT 3
(0.2ms) BEGIN
SQL (0.2ms) DELETE FROM "roles" WHERE "roles"."id" = $1 [["id", 5492]]
(6.6ms) COMMIT
(0.2ms) BEGIN
SQL (0.2ms) DELETE FROM "roles" WHERE "roles"."id" = $1 [["id", 5491]]
(0.4ms) COMMIT
(0.1ms) BEGIN
SQL (0.1ms) DELETE FROM "roles" WHERE "roles"."id" = $1 [["id", 5490]]
(0.2ms) COMMIT
The DELETE
parts are exactly the same. They both took multiple SQL queries.
The only difference is SELECT
part, if we change 'created_at DESC'
to 'id DESC'
, they will be exactly the same too.