为此,您可以采取几种方法。尽管 4000 条记录在计划中并不是那么多,但避免在多个查询中访问(就像您在问题中暗示的那样)是一件“好事”。
解决方案的关键是如何选择要查看的地址。“第一”是相对的和非确定性的,没有限定符(排序依据或其他属性)。
简单方法 - has_one 关联
一种简单的方法是定义一个 has_one 关系,例如
class User
has_many :addresses
has_one :primary_address, :class_name => 'Address', :conditions => ["primary = ?", true]
end
假设您在 Address 中有一个属性,您可以使用决定器。如果您不指定条件,您将只获得“第一个”出现的地址条目,因为 has_one 将 SQL LIMIT 1 应用于基础查询。
这使得收集用户和单个地址详细信息变得非常容易:
# iterating over users
User.scoped.each { |user| puts user.primary_address.area_code }
# or collect area_codes
area_codes = User.scoped.collect{ |user| user.primary_address.area_code }
但是,这仍然是访问每一行,因此每个用户的地址都会有一个额外的查询(和 ruby 数组处理)。
好多了-Arel
一旦您选择了用户可能拥有的多个地址之间的决定标准,就可以将其汇总到一个查询中。
如果我假设我们只通过 min(id) 选择地址 - 并假设你在 Rails 3 上 - 这是查询不同 area_codes 的 Arel 方法:
address = Address.arel_table
primary_addresses = Address.where(
address[:id].in(
address.group(address[:user_id]).project(address[:id].minimum)
)
)
primary_addresses 是一个范围,可为您提供用户“主要”地址的完整集合。然后,您可以根据需要处理/列出/收集,例如
area_codes = primary_addresses.collect(&:area_code)
该查询实际上会生成如下 SQL:
SELECT "addresses".*
FROM "addresses"
WHERE "addresses"."id" IN (
SELECT MAX("addresses"."id") AS max_id FROM "addresses" GROUP BY "addresses"."user_id"
)
如您所见,子选择正在返回地址 id 的列表 - 每个用户一个。如果您想使用不同的标准来选择要使用的地址,您可以更改子查询(例如,使其在 primary=true 上选择,限制为 1)