运行两个类似的查询,如
@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
需要内存中的集合,所以第一个语句被强制运行。任何人都可以描述这里发生了什么,如果它的主题过于宽泛,请指出我的资源。
答案 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)
,您会看到另一个查询,依此类推。