【问题标题】:Problems with Rails 3 Active Record Query Interface with join on IDRails 3 Active Record Query Interface with join on ID 的问题
【发布时间】:2012-06-11 19:30:31
【问题描述】:

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”

这是我的预期。

=== 使用自定义左连接的 Active Record 查询接口

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 子句)相同。这告诉我 AND 子句在 Active Record 查询接口中被忽略了。

=== 使用 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,这是预期的效果,还是我遗漏了什么?

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

【问题讨论】:

    标签: sql ruby-on-rails-3 postgresql activerecord arel


    【解决方案1】:

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

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

    见:Heroku demo page (skip to bottom)

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

    【讨论】:

      【解决方案2】:

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

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

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

      我会做的事情是这样的:

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

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

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

      HTH

      【讨论】:

      • 我并不专注于在一次大规模连接中获取所有内容。如果您在 github 项目上注意到,我尝试了 where 子句,它没有返回所有查找项,也没有起到 sql where 子句的作用。请查看 heroku 页面。我会投票给你,但我没有这样做的排名。
      • 我在 where 子句上使用了包含的哈希值(检查上面的 github 或 heroku)。现在它的功能类似于 SQL where 子句(这并不能解决我的问题)。
      猜你喜欢
      • 2011-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-12
      • 1970-01-01
      相关资源
      最近更新 更多