2

我们有一个相当简单的查询,它与 AR 查询结构完美配合:

user = User.includes({:network => :product}).joins({:network => :product}).where(:id => user_id).first

这将在数​​据库上执行单个查询,填充急切加载的集合(即:我可以user.product.network.name在不调用数据库的其他查询的情况下调用)。

我们想向User对象中添加一些“伪”列,这些列在用户表中不存在,但我们希望作为初始查询的一部分进行填充。SQL 如下所示:

SELECT 
    "users".*,
    "networks".*,
    "products".*, 
    (select count("spaces".id) from spaces "spaces" where "spaces".owner_id = "users".id) as spaces_count
FROM
    users "users" 
JOIN
    networks "networks" 
    on "networks".id = "users".network_id 
JOIN products "products" 
    on "products".id="networks".product_id 
WHERE "users".id=?

用(未优化的)内部查询添加的“spaces_count”列是现阶段唯一的“pseduo”列,但视图是要添加更多。

User类(一个 ActiveRecord 模型)中有:

attr_accessible :spaces_count

为了执行 SQL 查询,我们使用以下内容:

user = find_by_sql(sql)
ActiveRecord::Associations::Preloader.new([user], {:network => :product}).run

其中 sql 是上面包含的查询。

结果是一个User模型,按预期填充,包括spaces_count“伪”列,但是作为调用run关联预加载器的一部分,它再次访问数据库两次:

1.9.3p125 :465 > ActiveRecord::Associations::Preloader.new([user], {:network => :product}).run
  Network Load (0.5ms)  SELECT "networks".* FROM "networks" WHERE "networks"."id" IN (1)
  Product Load (0.3ms)  SELECT "products".* FROM "products" WHERE "products"."id" IN (1)
=> [{:network=>:product}] 

有没有一种方法可以ActiveRecord::Associations::Preloader用来急切地获取关联,而无需额外多次访问数据库,按照它的方式includesjoins

我们正在使用 Rails 3.2、Ruby 1.9.3-p125 和 PG 9.1。

4

1 回答 1

1

我不知道是否有办法完全按照您的要求做,我知道您不必这样做 - 只需使用更多那些 AR“查询构造”。

首先,您可以指定自定义选择:

users = User.includes({:network => :project}).select('users.*, (select count("spaces".id) from spaces "spaces" where "spaces".owner_id = "users".id) as spaces_count').joins({:network => :product}).where(:id => user_id).first

您可以按任何顺序添加这些内容 - 我的个人惯例是“包含”首先出现,然后按照查询中出现的顺序排列其他所有内容,但这只是我。这仍然会获得您需要的急切加载,并且您可以通过在返回的对象上调用它们的名称来获取“伪列”的值:

users.first.spaces_count

注意 PostgreSQL 适配器似乎将这些值作为字符串返回,无论它们在数据库中的实际类型是什么,因此您可能需要在之后转换它们:

users.first.spaces_count.to_i

最后一点,虽然我确实希望这个解决方案对您有用,但我也对您建议的方法的答案感兴趣 - 通过 AR::Associations::Preloader 自定义急切加载听起来既酷又有用。

于 2012-10-10T15:18:15.693 回答