我一直认为我应该使用.joins
或.includes
,但不能同时使用同一查询。
假设我们在数据库中有一个包含3个表的模式。
我有一个User
型号。
User
has_many Contact
Contact
has_many ContactEvent
请注意,有些Contacts
有0 Contactevents
,有些有很多!
我想获得一个特定的User
,获取他的每个Contacts
并计算每个ContactEvent
有多少Contact
。
user = User.find_by_id(1)
user.contacts.map{|contact| contact.contact_events.size}
这会产生大量的查询并对内存造成巨大负担(它必须加载用户在内存中的所有50k联系人,然后为每个联系人启动查询)。
性能非常差,因为它加载了所有Contacts
,完全没有被要求,因为,例如,来自50k Contacts
只有3k的ContactEvents
。
Contact.joins(:contact_events).where("user_id = 1").uniq.map{|c| c.contact_events.size}
这个速度要快得多,因为它首先只加载已经有Contacts
的{{1}},但仍然需要10秒钟,并且控制台中有大量查询试图获取{{每个ContactEvents
1}}。示例:contact_events
由于我注意到之前的尝试是针对每个:contact_events进行额外查询,因此我提示使用contact
但如果我只是将(7.0ms) SELECT COUNT(*) FROM contact_events WHERE contact_events.contact_id = 2052447
替换为.includes
,我发现它不会仅返回.joins
.includes
,而是会返回所有Contacts
。
我终于通过使用以下行获得了我想要的东西:
ContactEvents
这是超快的,只有一个查询,需要不到一秒钟。
这是正确的做法吗?对同一个查询使用.joins和.includes吗?
答案 0 :(得分:0)
您可以选择更有效地计算contact_events
。
选项1:
使用LEFT {OUTER} JOIN
表示如果contact
没有contact_events
则会显示0.当您认为要消失{{1}时,可以使用INNER JOIN
当没有contact
时。
contact_events
选项2:
您可以使用counter_cache
Contact.select("contacts.*, COUNT(contact_events.contact_id) as contact_events_count")
.join("LEFT JOIN contact_events ON contacts.id = contact_events.contact_id")
.group("contacts.id")
最后,要获得专柜大小,您可以致电:
<强>控制器强>
# Contact
has_many :contact_events
# Contact Event
belongs_to :contact, counter_cache: true
查看强>
@contacts = Contact.all
我希望这对你有所帮助。