Raills:获取delete_all生成的SQL

时间:2015-11-10 21:51:40

标签: sql ruby-on-rails ruby activerecord rails-activerecord

我对Ruby on Rails并不是特别熟悉,但是我正在解决一个我们正在经历的应该是清理数据库表的rake作业的问题。这些表变得非常快,ActiveRecord生成的查询似乎没有足够的效率来处理它。

Ruby调用如下所示:

Source.where("id not IN (#{Log.select('DISTINCT source_id').to_sql})").delete_all

和此:

Log.joins(:report).where(:report_id => Report.where(cond)).delete_all

我试图了解SQL,因此我们可以让DBA尝试更好地优化它。我注意到我放弃了#34; .delete_all"我可以添加一个" .to_sql"在调用" .delete_all"之前,它给出了查询的SELECT语句。我想看看那个delete_all方法正在生成什么SQL。

有办法吗?

3 个答案:

答案 0 :(得分:1)

来自fine manual

  

delete_all(conditions = nil)

     

删除匹配conditions的记录,而不首先实例化记录,因此不调用destroy方法也不调用回调。这是一个直接进入数据库的SQL DELETE语句,比destroy_all更有效。

因此Model.delete_all(conditions)最终为

delete from models where conditions

当您说Model.where(...).delete_all时,conditions的{​​{1}}来自delete_all来电,所以这些是相同的:

where

将其应用于您的案例:

Model.delete_all(conditions)
Model.where(conditions).delete_all

你应该看到你正在运行:

Source.where("id not IN (#{Log.select('DISTINCT source_id').to_sql})").delete_all

如果您在开发控制台中运行代码,您应该在控制台或Rails日志中看到SQL,但它将如上所示。

就优化而言,我的第一步是删除DISTINCT。 DISTINCT通常不便宜,delete from sources where id not in ( select distinct source_id from logs ) 无论如何都不关心重复,所以IN可能是毫无意义的繁忙工作。然后,not in (select distinct ...)上的索引可能会有所帮助,查询优化器可能会直接从索引中剔除source_id列表,而无需进行表扫描来查找它们。当然,查询优化有点暗淡,所以这些简单的步骤可能会或可能不会起作用。

答案 1 :(得分:1)

ActiveRecord::Base.logger = Logger.new(STDOUT)应该会显示控制台上rails生成的所有SQL。

答案 2 :(得分:0)

另一种选择是使用原始Arel语法,类似于ActiveRecord::Relation#delete_all的简化版本。

.as-console-wrapper {
  max-height: 100% !important;
}

这将为您提供生成的删除SQL。这是一个使用postgres作为sql适配器的示例

relation = Model.where(...)
arel = relation.arel
stmt = Arel::DeleteManager.new
stmt.from(arel.join_sources.empty? ? Model.arel_table : arel.source)
stmt.wheres = arel.constraints
sql = Model.connection.to_sql(stmt, relation.bound_attributes)
print sql