是否可以批量级联删除活动记录?
Is it possible to cascade active record deletion in batches?
我有很多数据要删除,因此我使用 delete/delete_all 而不是销毁,通过外键级联 (on_delete: :cascade
)。
我想删除一个父活动记录,它有一对 "child tables" 多行。其中一些子表还有几个子表。为此,我在外键上添加了级联,这样我只需要调用 parent.delete 即可触发删除父项的所有子项和孙子项。
我想将 delete/delete_all 与活动记录批合并 https://api.rubyonrails.org/classes/ActiveRecord/Batches.html,但由于只有一个父级,我不确定如何巧妙地合并批和级联删除。
一种选择是显式批量删除子孙,例如
parent.children_x.find_each do |child_x|
child_x.grand_children_y.in_batches do |grand_child_y_batch|
grand_child_y_batch.delete_all
end
child_x.delete
end
parent.children_z.in_batches do |child_batch|
child_batch.delete_all
end
...etc...
但如果有一种更隐式的方式允许我只对父级调用删除并批量删除子级和孙级,那将是更可取的,例如
parent.cascade_in_batches do |parent_batch|
parent_batch.delete_all #This batch deletes all children and grand children
end
我看到 parent 上没有 in_batches
,因为 parent 只是一个实体,所以看起来只有像上面第一个例子那样显式地批量删除才有可能吗?
谢谢,
-路易丝
您真的只需要将外键设置为级联,Postgres 将负责一路删除。由于这是在数据库层上实现的,因此您如何触发从 Rails.
中删除并不重要
class CreateCountries < ActiveRecord::Migration[6.0]
def change
create_table :countries do |t|
t.string :name
t.timestamps
end
end
end
class CreateStates < ActiveRecord::Migration[6.0]
def change
create_table :states do |t|
t.string :name
t.belongs_to :country, null: false, foreign_key: {on_delete: :cascade}
t.timestamps
end
end
end
class CreateCities < ActiveRecord::Migration[6.0]
def change
create_table :cities do |t|
t.string :name
t.belongs_to :state, null: false, foreign_key: {on_delete: :cascade}
t.timestamps
end
end
end
型号:
class Country < ApplicationRecord
has_many :states
has_many :cities, through: :states
end
class State < ApplicationRecord
belongs_to :country
has_many :cities
end
class City < ApplicationRecord
belongs_to :state
has_one :country, through: :state
end
通过规范:
require 'rails_helper'
RSpec.describe Country, type: :model do
describe "cascading delete" do
let!(:country){ Country.create }
let!(:state){ country.states.create }
let!(:city){ state.cities.create }
it "deletes the states" do
expect {
country.delete
}.to change(State, :count).from(1).to(0)
end
it "deletes the cities" do
expect {
Country.delete_all
}.to change(City, :count).from(1).to(0)
end
end
end
是否使用 .each_with_batches
与此无关。任何创建 DELETE FROM countries
查询的内容都会触发该数据库触发器。除非你真的需要评估是否每个 parent 应该在 Rails 中删除你应该能够做到:
Country.where(evil: true).delete_all
这将比 .find_each
更有效,因为您只需要执行一个 SQL 查询。如果您遍历记录,您正在对每行执行一个 DELETE FROM coutries WHERE id = ?
查询,并且由于它的阻塞 Rails 必须等待到数据库的往返。
我有很多数据要删除,因此我使用 delete/delete_all 而不是销毁,通过外键级联 (on_delete: :cascade
)。
我想删除一个父活动记录,它有一对 "child tables" 多行。其中一些子表还有几个子表。为此,我在外键上添加了级联,这样我只需要调用 parent.delete 即可触发删除父项的所有子项和孙子项。
我想将 delete/delete_all 与活动记录批合并 https://api.rubyonrails.org/classes/ActiveRecord/Batches.html,但由于只有一个父级,我不确定如何巧妙地合并批和级联删除。
一种选择是显式批量删除子孙,例如
parent.children_x.find_each do |child_x|
child_x.grand_children_y.in_batches do |grand_child_y_batch|
grand_child_y_batch.delete_all
end
child_x.delete
end
parent.children_z.in_batches do |child_batch|
child_batch.delete_all
end
...etc...
但如果有一种更隐式的方式允许我只对父级调用删除并批量删除子级和孙级,那将是更可取的,例如
parent.cascade_in_batches do |parent_batch|
parent_batch.delete_all #This batch deletes all children and grand children
end
我看到 parent 上没有 in_batches
,因为 parent 只是一个实体,所以看起来只有像上面第一个例子那样显式地批量删除才有可能吗?
谢谢,
-路易丝
您真的只需要将外键设置为级联,Postgres 将负责一路删除。由于这是在数据库层上实现的,因此您如何触发从 Rails.
中删除并不重要class CreateCountries < ActiveRecord::Migration[6.0]
def change
create_table :countries do |t|
t.string :name
t.timestamps
end
end
end
class CreateStates < ActiveRecord::Migration[6.0]
def change
create_table :states do |t|
t.string :name
t.belongs_to :country, null: false, foreign_key: {on_delete: :cascade}
t.timestamps
end
end
end
class CreateCities < ActiveRecord::Migration[6.0]
def change
create_table :cities do |t|
t.string :name
t.belongs_to :state, null: false, foreign_key: {on_delete: :cascade}
t.timestamps
end
end
end
型号:
class Country < ApplicationRecord
has_many :states
has_many :cities, through: :states
end
class State < ApplicationRecord
belongs_to :country
has_many :cities
end
class City < ApplicationRecord
belongs_to :state
has_one :country, through: :state
end
通过规范:
require 'rails_helper'
RSpec.describe Country, type: :model do
describe "cascading delete" do
let!(:country){ Country.create }
let!(:state){ country.states.create }
let!(:city){ state.cities.create }
it "deletes the states" do
expect {
country.delete
}.to change(State, :count).from(1).to(0)
end
it "deletes the cities" do
expect {
Country.delete_all
}.to change(City, :count).from(1).to(0)
end
end
end
是否使用 .each_with_batches
与此无关。任何创建 DELETE FROM countries
查询的内容都会触发该数据库触发器。除非你真的需要评估是否每个 parent 应该在 Rails 中删除你应该能够做到:
Country.where(evil: true).delete_all
这将比 .find_each
更有效,因为您只需要执行一个 SQL 查询。如果您遍历记录,您正在对每行执行一个 DELETE FROM coutries WHERE id = ?
查询,并且由于它的阻塞 Rails 必须等待到数据库的往返。