2

我试图只访问关联表的第一行。最好和最快的方法是什么?(理想情况下是一条 SQL 语句)

协会:

class User
  has_many :addresses


class Address
  belongs_to :user

我能想到的最快方法是:

User.includes(:addresses).find(:all).map {|m| m.addresses.first.area_code}

但是这个操作对我的要求来说太慢了(想象一下我200 Users的平均20 Adresses~4000 Adresses整体))

这只是我正在尝试做的一个例子。

我认为这可以通过 SQL JOIN 语句来表达,但我没有看到如何接收每个用户第一个地址的表。

预期输出示例:

12345 #=>User.find(1).addresses.first.area_code

56789 #=>User.find(2).addresses.first.area_code 

23456 #=>User.find(3).addresses.first.area_code 

.....
4

2 回答 2

0
area_codes = 
  Address.find_by_sql(
    "SELECT DISTINCT user_id, area_code FROM addresses"
  ).index_by(&:user_id)
User.find(:all).map{ |u| [u, area_codes[u.id]] }

如果你想把它们全部合二为一。

于 2011-06-27T17:33:18.120 回答
0

为此,您可以采取几种方法。尽管 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)

于 2011-10-04T22:07:32.207 回答