3

我基本上没有使用 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 查询,它返回的正是我想要的:一个包含文本类型描述的图纸列表。

在我的一生中,我无法让 Rails 生成和使用这样的单个查询。我能做的最好的就是让它做两个查询,这清楚地表明它在代码中做“加入”,而不是让数据库去做。有时它喜欢做 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 查询,一个 on drawings.*,另一个用于获取所有描述,然后在代码中清楚地查找这些描述。如果我将其更改includes为 ajoins它会执行 1+M+N 个 SQL 查询(M = 绘图类型数,N = 绘图数)!?!?!?!

我可以通过使用它(从这里)向日志发出正确的 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 连接,它们都是做这个简单事情的错误方式。

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

谢谢,克里斯

4

2 回答 2

2

我最近在工作中遇到了同样的问题,发现使用.joins()and可以做到这一点.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 上有一个字段“描述”,而不是“类型描述”,但这应该是唯一的区别。

以及View中对应的代码:

<% @drawings.each do |drawing| %>
  <tr>
    <%= content_tag, :td, drawing.name %>
    <%= content_tag, :td, drawing.description %>
  </tr>
<% end %>
于 2013-06-15T14:38:17.940 回答
1

这种行为是有意的,因为在许多情况下,使用两个查询会比一个运行得更快。请参阅此答案以获得对推理的良好解释:How do I Avoid multiple queries with :include in Rails?

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

于 2013-06-15T10:19:39.500 回答