Rails ActiveRecord - 性能 - 如何销毁无效的 belongs_to 记录
Rails ActiveRecord - Performance - How to destroy invalid belongs_to records
我有两个模型:
- 资产has_many资产历史
- 资产历史belongs_to资产
不幸的是,创建迁移时,没有 foreign_key 添加到 Assethistories。存在一些资产历史记录,其中 Assethistories.asset_id 具有 Asset.id 中不存在的值(可能有人对资产 table 使用了删除而不是销毁)。
大约有 1000 万条资产记录和 2500 万条资产历史记录。
使用此查询需要很长时间:
Assethistory.where("asset_id NOT IN (select id from assets)").delete_all
or in rails syntax:
Assethistory.where.not(asset_id: Asset.select(:id)).delete_all
注意:我们可以使用 delete_all,因为没有回调或嵌套模型。
事实上,仅对无效记录执行 COUNT 个操作需要很长时间。
Assethistory.where.not(asset_id: Asset.select(:id)).count
有没有什么方法可以更高效地销毁无效记录?
我有一些数据比你的情况小 20 倍,例如 5M assethistory
和 500K asset
像你的情况,这可以通过使用 where.not
重现你的问题.通过遵循 Anti-Join
模式,其中加入不存在的记录,将为您带来您想要的结果和更好的性能。
Assethistory.left_outer_joins(:asset).where(assets: { id: nil })
# => 3500 rows (415.3ms)
或
Assethistory.where('NOT EXISTS (:assets)', assets: Asset.select('1').where('assets.id = assethistories.asset_id'))
# => 3500 rows (701.6ms)
通过使用 LEFT OUTER JOIN
而不是 WHERE NOT
,数据库以一种非常不同的方式扫描 table。 WHERE NOT
遍历 assethistories
中的所有行,其中 assethistories.asset_id
不匹配所有现有的 assets.id
.
LEFT OUTER JOIN
从 joined-table(assethistories
and assets
as a big one table) 中查询记录,其中行有 null
值在 asset.id
列中,效率更高。
我有两个模型:
- 资产has_many资产历史
- 资产历史belongs_to资产
不幸的是,创建迁移时,没有 foreign_key 添加到 Assethistories。存在一些资产历史记录,其中 Assethistories.asset_id 具有 Asset.id 中不存在的值(可能有人对资产 table 使用了删除而不是销毁)。
大约有 1000 万条资产记录和 2500 万条资产历史记录。
使用此查询需要很长时间:
Assethistory.where("asset_id NOT IN (select id from assets)").delete_all
or in rails syntax:
Assethistory.where.not(asset_id: Asset.select(:id)).delete_all
注意:我们可以使用 delete_all,因为没有回调或嵌套模型。
事实上,仅对无效记录执行 COUNT 个操作需要很长时间。
Assethistory.where.not(asset_id: Asset.select(:id)).count
有没有什么方法可以更高效地销毁无效记录?
我有一些数据比你的情况小 20 倍,例如 5M assethistory
和 500K asset
像你的情况,这可以通过使用 where.not
重现你的问题.通过遵循 Anti-Join
模式,其中加入不存在的记录,将为您带来您想要的结果和更好的性能。
Assethistory.left_outer_joins(:asset).where(assets: { id: nil })
# => 3500 rows (415.3ms)
或
Assethistory.where('NOT EXISTS (:assets)', assets: Asset.select('1').where('assets.id = assethistories.asset_id'))
# => 3500 rows (701.6ms)
通过使用 LEFT OUTER JOIN
而不是 WHERE NOT
,数据库以一种非常不同的方式扫描 table。 WHERE NOT
遍历 assethistories
中的所有行,其中 assethistories.asset_id
不匹配所有现有的 assets.id
.
LEFT OUTER JOIN
从 joined-table(assethistories
and assets
as a big one table) 中查询记录,其中行有 null
值在 asset.id
列中,效率更高。