Rails是否自动优化查询

时间:2014-12-26 21:38:00

标签: ruby-on-rails postgresql optimization rails-activerecord

运行两个类似的查询,如

@articles = @magazine.articles.limit(2).offset(0)
@articles = @articles.limit(2).offset(2)

我希望在我的控制台中看到两个由服务器执行的SQL语句。但是,第一个查询丢失,只有第二个查询正在运行。同样,执行以下两个查询后:

@articles = @magazine.articles.limit(2).offset(0)
@articles = @articles.limit(2).offset(@articles.size - 2)

第一个查询也完全被忽略。这两个查询生成SQL:

SELECT COUNT(count_column) FROM (SELECT  1 AS count_column FROM "articles"  
WHERE "articles"."magazine_id" = $1 LIMIT 2 OFFSET 0)
subquery_for_count  [["magazine_id", 1]]

SELECT  "articles".* FROM "articles"  
WHERE "articles"."magazine_id" = $1 
LIMIT 2 OFFSET 2  [["magazine_id", 1]]

有趣的是,如果我将@articles.size更改为@articles.length,则两个查询都会按预期运行。我认为,因为length需要内存中的集合,所以第一个语句被强制运行。任何人都可以描述这里发生了什么,如果它的主题过于宽泛,请指出我的资源。

1 个答案:

答案 0 :(得分:2)

优化不如推迟执行查询,直到真正需要执行它。

在这两种情况下,您都会在@articles中存储构建查询的结果。 Active Record,或更准确地说是arel,推迟执行查询,直到您调用需要结果的方法。我怀疑当你调用类似@artircles.each@articles.count或类似的东西时,你实际上是在看到对数据库执行的查询。

您可以通过一系列步骤构建查询,但实际上不会执行:

a = @magazine.articles
a = a.limit(2)
a = a.offset(0)

这也意味着您可以留下一些查询子句,将结果大小大幅减少到流程结束:

a = a.where('created_at > ?', Time.now.at_beginning_of_day)

仍未向数据库发送任何查询。

需要注意的是在rails控制台中测试这个逻辑。如果在控制台本身中运行这些步骤,它会尝试显示最后一个返回值(通过调用.inspect我认为),并通过检查返回值导致查询执行。因此,如果您将a = Magazine.find(1).articles放入控制台,您将看到一个立即执行的查询,如果代码是在控制器操作的上下文中运行的话,那将是不可能的。如果您随后致电a.limit(2),您会看到另一个查询,依此类推。