如何让rails为此迭代执行正确的单个SQL连接查询?

时间:2013-06-15 08:18:15

标签: ruby-on-rails rails-activerecord

我基本上没有使用Rails的经验,而且我正在努力帮助一位正在学习如何编程的朋友,但我对SQL有很多经验,而且我很难让Rails做正确的事情对于带连接的简单查询。

我有两个表,drawingsdrawing_typesdrawings有一个指向drawing_types的外键,其中包含type_description列。这显然是你能想象到的最简单的骨骼关系。在SQL中,我这样做是为了使用正确的type_descriptions

获取所有图形
SELECT d.*, dt.type_description FROM drawings as d
INNER JOIN drawing_types as dt ON dt.id = d.drawing_type_id;

这是一个单独有效的SQL查询,可以完全返回我想要的内容:一个也有文本类型描述的图纸列表。

对于我的生活,我不能生成并使用像这样的单个查询。我能做的最好的事情是让它做两个查询,这清楚地表明它在代码中进行“连接”,而不是让数据库执行它。有时它喜欢做N + 1个查询!?!

以下是该模型的代码:

class Drawing < ActiveRecord::Base
  attr_accessible :image, :drawing_type_id
  belongs_to :drawing_type
end

class DrawingType < ActiveRecord::Base
  attr_accessible :type_description
  has_many :drawings
end

以下是相关的控制器代码:

@drawings = Drawing.includes(:drawing_type)

以下是观点:

<% @drawings.each do |drawing| %>
  <tr>
    <td><b><%= drawing.drawing_type.type_description %></b></td>

这将生成两个SQL查询,一个在drawings.*上,另一个用于获取所有描述,然后在代码中清楚地查找。如果我将includes更改为joins,它会执行1 + M + N个SQL查询(M =绘图类型数,N =绘图数)!?!?!?!

我可以通过使用它(来自here)让它向日志发出正确的SQL连接:

@drawings = Drawing.select("drawings.*, drawing_types.type_description").
     joins("INNER JOIN drawing_types ON drawing_types.id = drawings.drawing_type_id")

但是,rails似乎只将结果用作图形数组,而忽略了结果中的type_description列,因此它会进行额外的M + N查询!

所有这些变体都“起作用”,即页面为所有这些变换正确,但相对于应该执行的单个正确的SQL连接,它们都是做这个简单事情的错误方法。

只需要一种简单的方法来正确地执行此操作,因为它是您在完全无聊的网站架构中可能找到的最基本的关系,但我已经搜索了高低,包括在这里阅读大量答案和所有文档,并且无法弄清楚如何做到这一点。

谢谢, 克里斯

2 个答案:

答案 0 :(得分:2)

我最近在工作中遇到了同样的问题,发现您可以使用.joins().select()执行此操作。我发现原型的最佳方法是在rails控制台中尝试不同的东西,直到得到你想要的查询。

控制器:

@drawings = Drawing.joins(:drawing_type).select([:name, :description]).all

这将产生:

Drawing Load (0.4ms)  SELECT name, description FROM `drawings`
  INNER JOIN `drawing_types` ON `drawing_types`.`id` = `drawings`.`drawing_type_id`

在我的例子中,我在DrawingType上有一个字段'description',而不是'type_description',但这应该是唯一的区别。

视图中的相应代码:

<% @drawings.each do |drawing| %>
  <tr>
    <%= content_tag, :td, drawing.name %>
    <%= content_tag, :td, drawing.description %>
  </tr>
<% end %>

答案 1 :(得分:1)

此行为是故意的,因为在许多情况下使用两个查询的速度会比一个查询快。有关推理的详细解释,请参阅此答案:How do I avoid multiple queries with :include in Rails?

也就是说,您可以通过在关系上指定 where 条件来强制执行单个查询,但不建议这样做。