The Kai Way

Pragmaticly hacking

After Eager Loading

| Comments

Eager Loading是ActiveRecord的一个数据查询的优化措施,在查询model的时候连同它的关联对象都全查询出来(通过一条包含LEFT OUTER JOIN的SQL语句)。但是在你使用了Eager Loading后,如果还是用一些会触发数据库本身的统计函数的查询方法(如count,average等)的话,那前面的Eager Loading就白费了。下面给出例子:

两个Model,Project和Task,关系是Project has_many Task。

在ProjectController的show中:

@project = Project.find(params[:id], :include => [:articles, :tasks])

这是project/show页面:

<h1><%=h @project.title %></h1> <h2><%=h @project.description %></h2> <p>统计信息:<br/>共有<%= @project.tasks.count %>个<%=link_to '任务', project_tasks_path(@project)%>, 已完成<%= @project.completed_tasks.length %>个任务。 <hr/> <h3>最新未完成的5个任务</h3> <table>   <tr>     <th width="25%">任务名</th>     <th width="75%">任务摘要</th>   </tr>   <% for task in @project.tasks.incompleted %>     <tr>       <td><%= link_to task.name, project_task_path(@project,task) %></td>       <td><%= task.content %></td>     </tr>   <% end %> </table>

来看看log吧:

Project Load Including Associations (0.000595)   SELECT `projects`.`id` AS t0_r0, `projects`.`title` AS t0_r1, `projects`.`description` AS t0_r2, `projects`.`created_at` AS t0_r3, `projects`.`updated_at` AS t0_r4, `articles`.`id` AS t1_r0, `articles`.`subject` AS t1_r1, `articles`.`summary` AS t1_r2, `articles`.`content` AS t1_r3, `articles`.`project_id` AS t1_r4, `articles`.`created_at` AS t1_r5, `articles`.`updated_at` AS t1_r6, `articles`.`lock_version` AS t1_r7, `tasks`.`id` AS t2_r0, `tasks`.`name` AS t2_r1, `tasks`.`content` AS t2_r2, `tasks`.`completed` AS t2_r3, `tasks`.`project_id` AS t2_r4, `tasks`.`created_at` AS t2_r5, `tasks`.`updated_at` AS t2_r6 FROM `projects` LEFT OUTER JOIN `articles` ON articles.project_id = projects.id LEFT OUTER JOIN `tasks` ON tasks.project_id = projects.id WHERE (`projects`.`id` = 2)
Rendering template within layouts/application
Rendering projects/show
SQL (0.000255)   SELECT count(*) AS count_all FROM `tasks` WHERE (tasks.project_id = 2)

可以看到在Project的Eager Loading的查询之后可以看到又有一个count()的select查询进行了。那前面的Eager Loading的效果似乎没有了。再试试把count方法调用改为调用length方法,这时就看不到另外的那个count()查询了。

这个可能在写代码的时候不会太留意,不过这对性能的影响还是有些的,如果Eager Loading后不会在调用统计函数就ok了。