本质上,我们需要删除@user
中没有t3
记录的T1
记录。虽然不是 * 必需的 *,但也可以删除没有T3
连接的T2
记录。
这就是被推送到生产环境的代码。很明显,它很棒,因为它通过了单元测试(哈!).
T1.where(user_id: @user.id, enabled: true)
.joins('LEFT JOIN t2 ON t2.t1_id = t1.id')
.joins('LEFT JOIN t3 ON t3.id = t2.t3_id')
.where('t3.id IS NULL').delete_all
字符串
生成的SQL:
DELETE FROM `t1`
WHERE `t1`.`id` IN
(SELECT id FROM
(SELECT `t1`.`id` FROM `t1`
LEFT JOIN t2 ON t2.t1_id = t1.id
LEFT JOIN t3 ON t3.id = t2.t3_id
WHERE `t1`.`user_id` = 65987
AND `t1`.`enabled` = 1
AND (t2.id IS NULL)
) __active_record_temp
);
型
我知道这是这里产生的SQL的唯一原因,是因为它包含在Server 500死锁错误中。* 在测试时,我似乎无法在控制台中显示delete_all
查询。* 我能够将查询输出转换为SELECT
,并解释,其中显示了最外面的select扫描了数百万行(我相信对于DELETE
操作来说,这意味着相同数量的行锁)。
问题:
1.在Rails with ActiveRecord中,基于连接值从一个或多个表中删除记录的最佳方法是什么?
1.我们有什么选择来检查/测试Rails中的SQL输出,以确保我没有性能差和死锁的风险?
更新:图变厚...添加当前关联
class User < ActiveRecord::Base
has_many :T1s
has_many :T2s
class T1 < ActiveRecord::Base
belongs_to :user
class T2Custom < ActiveRecord::Base
self.table_name = "t2"
has_many :T3s, :foreign_key => :t2_id
class T3 < ActiveRecord::Base
belongs_to :T2, foreign_key: "t2_id"
belongs_to :T1
型
3条答案
按热度按时间g2ieeal71#
使用
PRIMARY KEY
遍历主表。对于每个块(比如1000行),在单独的事务中执行UPDATE
。DELETE
编写的,但可以适用于UPDATE
。如果你不需要,不要使用
LEFT
。确保您有合适的索引,以避免在
JOINs
上进行表扫描。如果这些表中的任何一个是多:多,请按照效率提示 * 这里 *。
a1o7rhls2#
1.您可以使用Active Record(https://apidock.com/rails/ActiveRecord/Batches/find_in_batches)
find_in_batches
方法来删除记录,并且它是可配置的。1.你可以在测试中使用子弹宝石来验证你没有n+1个查询(https://github.com/flyerhzm/bullet),但我不确定这是你的问题。
您可以使用Active Record(https://apidock.com/rails/ActiveRecord/Relation/to_sql)
.to_sql
函数来解释任何查询。9q78igpj3#
作为一个快速(可能是永久的)修复,我决定用
.destroy_all
替换.delete_all
,它基本上只运行内部查询,扫描27行,然后逐个示例化和删除记录,如果需要的话,还可以运行destroy
回调。LEFT JOIN
的作用是查找此时由于其他表中的子记录从未被创建而无效的记录。作用域中的记录数不应超过30。代码试图查找并删除不应该存在的记录。这意味着,95%的情况下,代码段将在(快速)初始查询返回空值后退出。按照ruby_newbie的建议运行
.to_sql
对解决这个问题非常有帮助。