1

我一直遇到 Rails 3 Active Record Query Interface 的问题。我有一个查找表 (lookups)、一个主表 (through_references) 和一个名为 through_tables 的直通/连接表。因此,这是我使用 has_many :through 设置的 HABTM 配置。

更新:这里需要特别注意的是,当我进行这些联接时,我一直在加入 ID,以提供记录过滤。这似乎不适用于 Active Record Query Interface。如果您不想看到我辛苦的血腥细节,您可以跳到下面查看我的解决方法。

我们还将有一些主要项目(through_references 表)应该能够有任何查找项的组合,并且能够方便地通过复选框单击相关的查找项。

我已经在github上发布了代码。github源代码上有很多解释。要查看结果,请转到查找索引页面。请注意,您将需要使用脚手架代码创建记录。

我还在heroku上启动并运行了代码,并提供了更多解释和示例。

class Lookup < ActiveRecord::Base
  has_many :fk_references
  has_many :through_tables
  has_many :through_references, :through => :through_tables
  attr_accessible :name, :value
end

class ThroughTable < ActiveRecord::Base
  belongs_to :through_reference
  belongs_to :lookup
  attr_accessible :description, :through_reference_id, :lookup_id
end

class ThroughReference < ActiveRecord::Base
  has_many :through_tables
  has_many :lookups, :through => :through_tables
  attr_accessible :description
end

如果我们想要列出所有查找项目以及与之对应的主要项目,我们可以将“查找”表与主要项目(through_references)表连接起来。对应的SQL:

SELECT * FROM lookups
  LEFT OUTER JOIN through_tables ON (lookups.id = through_tables.lookup_id AND through_tables.through_reference_id = 1)
  LEFT OUTER JOIN through_references ON through_references.id = through_tables.through_reference_id
  ORDER BY lookups.id

返回记录:

1;“Lookup Item 1”;“1”;“2012-06-06 17:14:40.819791”;“2012-06-06 17:14:40.819791”;1;1;1;“Main Item 1 has Lookup item 1”;“2012-06-06 17:17:31.355425”;“2012-06-06 17:17:31.355425”;1;“Main Item 1”;“2012-06-06 17:16:30.004375”;“2012-06-06 17:16:30.004375”

2;“Lookup Item 2”;“2”;“2012-06-06 17:14:59.584756”;“2012-06-06 17:14:59.584756”;;;;“”;“”;“”;;“”;“”;“”

3;“Lookup Item 3”;“3”;“2012-06-06 17:15:14.700239”;“2012-06-06 17:15:14.700239”;2;1;3;“Main Item 1 has Lookup item 3”;“2012-06-06 17:17:53.169715”;“2012-06-06 17:17:53.169715”;1;“Main Item 1”;“2012-06-06 17:16:30.004375”;“2012-06-06 17:16:30.004375”

这是我所期望的。

=== 使用自定义左连接的活动记录查询接口

Lookup.joins(“LEFT OUTER JOIN through_tables ON (lookups.id = through_tables.lookup_id AND through_tables.through_reference_id = 1)” ).includes(:through_references).order(‘lookups.id’)

从 Active Record 查询界面返回的内容(注意我在 Active Record 层次结构中向下导航):

Lookup ID Lookup Name Lookup Value Through Table ID Through Table Description Main Item ID Main Item Description
1 Lookup Item 1 1 1 Main Item 1 has Lookup item 1 1 Main Item 1
1 Lookup Item 1 1 3 Main Item 2 has Lookup item 1 2 Main Item 2
2 Lookup Item 2 2 4 Main Item 2 has Lookup item 2 2 Main Item 2
3 Lookup Item 3 3 2 Main Item 1 has Lookup item 3 1 Main Item 1

这不是我所期望的。

我们这里的内容与简单的左连接(没有 AND 子句)相同。这告诉我在 Active Record 查询界面中忽略了 AND 子句。

=== 使用 find_by_sql 方法的 Active Record 查询接口

Lookup.find_by_sql("SELECT * FROM lookups LEFT OUTER JOIN through_tables ON (through_tables.lookup_id = lookups.id AND through_tables.through_reference_id = 1) LEFT OUTER JOIN through_references ON through_references.id = through_tables.through_reference_id ORDER BY lookups.value, through_references.id" )

从 Active Record 查询界面返回的内容(注意我在 Active Record 层次结构中向下导航)::

Lookup ID   Lookup Name     Lookup Value    Through Table ID    Through Table Description   Main Item ID    Main Item Description
1   Lookup Item 1   1   3   Main Item 2 has Lookup item 1   2   Main Item 2
1   Lookup Item 1   1   1   Main Item 1 has Lookup item 1   1   Main Item 1
    Lookup Item 2   2   No through_tables entry
1   Lookup Item 3   3   3   Main Item 2 has Lookup item 1   2   Main Item 2
1   Lookup Item 3   3   1   Main Item 1 has Lookup item 1   1   Main Item 1

这里的结果很疯狂!

这是一个BUG,这是预期的效果,还是我错过了什么?

我希望有一种干净的方法可以做到这一点,而不必生成两个结果集,并通过代码将它们合并。

4

2 回答 2

1

我找到了解决方法。问题似乎是 Active Record 无法识别过滤 ID 的联接(LEFT OUTER JOIN xyz ON xyz.id = ID)。

我的解决方法包括创建一个存储过程或函数,将 ID 作为参数,在数据库中进行连接,并返回一个漂亮的平面记录集。

请参阅:Heroku 演示页面(跳到底部)

请注意,我没有将此标记为解决方案,因为这是一种解决方法,与活动记录无关。

于 2013-01-23T17:44:41.353 回答
0

好吧,阅读 github 项目,我看到了这个:

我真正想做的是列出所有查找项,如果有匹配的主要项,将它们附加到返回的记录中,如果没有,我想要空值。这是我使用了 10 多年的技术。

我认为问题正是你想这样做,当让 Rails 急切加载处理它会更自然,所以你已经专注于在单个大规模连接中获取所有内容。

我会做的是这样的:

Lookup.where( .. 在此处插入任何需要的条件 ...).includes(:through_tables)

然后 ActiveQuery 将在一个查询中获取所有查找,然后使用预加载来获取包含语句中命名的任何关联,每个关联一个查询。

请注意,我并不是说连接不好,只是说这是在 Rails 中更自然的方法。我喜欢使用 Preloader http://apidock.com/rails/ActiveRecord/Associations/Preloader将关于急切加载什么的决定与关于获取哪些数据的决定分开。我发现这对控制器很有帮助——让模型决定条件是什么,但让控制器决定它需要哪些对象来急切加载。

高温高压

于 2012-06-07T18:36:19.357 回答