我有这样的架构。
managers
has_many :emails
has_many :stores
emails
belongs_to :manager
stores
belongs_to :manager
belongs_to :region
regions
has_many :stores
has_many :readings
readings
belongs_to :regions
我想获得经理的阅读材料。在SQL中我会做这样的事情。
SELECT * FROM managers
JOIN stores ON stores.manager_id = managers.id
JOIN regions ON stores.region_id = regions.id
JOIN readings ON readings.region_number = regions.number
WHERE
manager.name = 'John Smith'
AND
regions.number = '1234567'
LIMIT 100
我无法弄清楚如何在activerecord中执行此操作。我一直试图理解http://guides.rubyonrails.org/active_record_querying.html和http://guides.rubyonrails.org/association_basics.html,但它并没有陷入其中。我想我只需要从不同的角度来看待它。
我以为我会访问这样的数据,但我想我不明白它是如何工作的。
managers.name
managers.stores.name
managers.stores.regions.readings.limit(10)
我一直都是这样的事情,这是一件非常丑陋的事。
managers.first.stores.first.regions.first.readings.limit(10)
答案 0 :(得分:44)
考虑以下模型(并使用has_many):
class Reading < ActiveRecord::Base
belongs_to :region,
inverse_of: :readings
end
class Region < ActiveRecord::Base
has_many :readings,
inverse_of: :region
has_many :stores,
inverse_of: :region
end
class Store < ActiveRecord::Base
belongs_to :region,
inverse_of: :stores
belongs_to :manager,
inverse_of: :stores
end
class Manager < ActiveRecord::Base
has_many :stores,
inverse_of: :region
has_many :emails,
inverse_of: :manager
has_many :regions,
through: :stores
has_many :readings,
through: :regions
end
class Email < ActiveRecord::Base
belongs_to :manager,
inverse_of: :emails
end
现在你的问题有点含糊不清,因为你说你想获得经理的读数,但你的SQL根本没有选择读数,也规定了一个区域。
假设您希望所有阅读匹配给定的经理和地区:
@readings = Reading.joins(region: { stores: :manager }).where(
manager: { name: 'John Smith' },
region: { id: 1234567 })
假设您还希望加载区域,商店和经理以避免1 + N查询:
@readings = Reading.includes(region: { stores: :manager }).where(
manager: { name: 'John Smith' },
region: { id: 1234567 })
假设您有经理姓名并且想要他们的详细信息和读数:
@manager = Manager.where(name: 'John Smith').first!
@readings = manager.readings
以上所有查询示例都会返回ActiveRecord::Relation
,可以使用where
条件进一步链接,或joins
,limit
,group
,{ {1}}和having
等
您还应该考虑order
,joins
,includes
,preload
和eager_load
方法的差异。有一个关于他们的简介here我也鼓励你阅读关于Arel的文档,指南和博客,因为它也支持连接和别名。
在愤怒中使用ActiveRecord一段时间后,我得出的结论是Sequel / SequelModel是比ActiveRecord好得多的DB / ORM。没有对开发者的不尊重,但我发现Sequel是一个更好的工具。 Arel多年来一直有瘦文档,ActiveRecord / Arel在连接条件,连接类型控制和热切加载,工会,交叉点,递归查询,树/邻接列表以及许多其他SQL功能等许多方面都存在缺陷覆盖。
由于你似乎刚开始使用AR,你可能希望从Sequel开始,而不是与弱点和ActiveRecord查询的挫折斗争,包括AR和Arel的脱节使用,关系与关联和查询组成的怪异,它继续下去。没有什么比知道你想要的SQL更令人沮丧但是ActiveRecord / Arel合谋阻止你,所以你被迫使用被吹捧的逃生路线并且'只使用SQL字符串片段'然后你得到一个无法链接的结果,但你的其余代码需要一个关系! (例如分页结果)
答案 1 :(得分:8)
我想评论来自 user1757006 2013年10月4日14:39 的评论,但我没有足够的分数。 无论如何,这解决了更深层次的嵌套方案。我添加了行星和国家模型,以显示在需要链接其他模型时语法如何工作。
@readings = Reading.includes(planet: [ country: [ region: [ {stores:
:manager}]]]).where(
manager: { name: 'John Smith' },
region: {id: 1234567 })
答案 2 :(得分:5)
尝试这样的事情:
managers.joins(stores: {regions: :readings}).where('managers.name = ? AND regions.number = ?', 'John Smith', '1234567').limit(100)
答案 3 :(得分:5)
假设managers
是型号名称
managers.find(:all,
:joins=>" JOIN stores ON stores.manager_id = managers.id
JOIN regions ON stores.region_id = regions.id
JOIN readings ON readings.region_number = regions.number"
:conditions=>"manager.name = 'John Smith' AND regions.number = '1234567'"
:limit=>100)