【问题标题】:Mean SQL in Rails 4 without SQL using joins and whereRails 4 中的平均 SQL 没有使用连接的 SQL 和 where
【发布时间】:2014-03-13 03:24:25
【问题描述】:

一个位置属于一个或多个实体。一个实体可以有一个或多个位置。

我尝试获取与当前位置具有相同实体的所有其他位置。

我有以下型号:

class Location < ActiveRecord::Base
  has_many :location_assignments
  has_many :entities, :through => :location_assignments
  accepts_nested_attributes_for :location_assignments
end

class Entity < ActiveRecord::Base
   has_many :location_assignments
   has_many :locations, through: :location_assignments
   accepts_nested_attributes_for :location_assignments
end

这是我想要的 SQL

SELECT DISTINCT l.* FROM locations l, location_assignments la, entities e
WHERE l.id = la.location_id
AND la.entity_id = e.id
AND e.id in ( SELECT ee.id from entities ee, location_assignments laa
WHERE ee.id = laa.entity_id
AND laa.location_id = 1)

但我不想使用 SQL。 这就是我用 Rails 尝试过的

Location.joins(:entities => :locations).where(:locations => {:id => location.id})

它给了我好几倍的当前位置。行数和 SQL 一样(没有 distinct 只获取当前位置)。

有什么想法吗?

【问题讨论】:

    标签: ruby-on-rails join model


    【解决方案1】:

    一种方法是模仿使用子查询的 SQL,只使用标准的 ActiveRecord 查询而不下降到 AREL。让我们从子查询开始:

    Entity.joins(:location_assignments).where(:location_assignments => {:location_id => location.id})
    

    这将返回一个包含所有实体的关系,这些实体的位置由 location.id 表示,与它们相关联,就像您的 SQL 子查询一样。

    然后是主查询,子查询现在为 xxxxx 以便于阅读:

    Location.joins(:entities).where(:entities => {:id => xxxxx})
    

    这相当于您的主要查询。插入返回基本上是实体数组的子查询(好吧,一个关系,但在这种情况下效果相同)提示 ActiveRecord 将 WHERE 条件转换为 IN 而不仅仅是 =。 ActiveRecord 也很聪明,可以使用每个实体的 id。所以,插入子查询:

    Location.joins(:entities).where(:entities => {:id => Entity.joins(:location_assignments).where(:location_assignments => {:location_id => location.id})})
    

    请注意,与您的查询一样,这会返回您开始时使用的原始位置,以及共享相同实体的所有其他位置。

    我认为这应该等同于您的 SQL,但看看它是如何处理您的数据的!

    如果您想使用自联接提高查询效率(这意味着您可以只使用 2 个联接,而不是使用 2 个联接和带有另一个联接的子查询)但不使用 SQL 片段字符串,我认为您可能需要下拉到 AREL 是这样的:

    l = Arel::Table.new(:locations)
    la = Arel::Table.new(:location_assignments)
    starting_location_la = la.alias
    l_joined_la = l.join(la).on(l[:id].eq(la[:location_id]))
    filtered_and_projected = l_joined_la.join(starting_location_la).on(starting_location_la[:entity_id].eq(la[:entity_id])).where(starting_location_la[:location_id].eq(location.id)).project(location[Arel.star])
    Location.find_by_sql(filtered_and_projected)
    

    这只是将所有位置连接到它们的位置分配,然后使用实体 ID 再次与位置分配连接,但仅与那些属于您的起始位置对象的位置连接,因此它就像一个过滤器。这给了我与使用标准 ActiveRecord 查询的先前方法相同的结果,但同样,看看它是如何处理您的数据的!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多