我对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。
有办法吗?
答案 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