使用find_by_sql复制包含&连接行为(构建关联的单个查询)

时间:2012-10-10 12:46:38

标签: ruby ruby-on-rails-3 postgresql activerecord

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

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

这会对数据库执行单个查询,填充急切加载的集合(即:我可以调用user.product.network.name而无需向DB调用其他查询。)

我们想在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热切地获取关联,而不是按照includes joins {{1}}的方式来追加数据库的额外时间?

我们使用的是Rails 3.2,Ruby 1.9.3-p125和PG 9.1。

1 个答案:

答案 0 :(得分: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

N.B。 PostgreSQL适配器似乎将这些值作为字符串返回,无论它们在数据库中的实际类型如何,因此您可能需要在以后转换它们:

users.first.spaces_count.to_i

作为最后一点,虽然我希望这个解决方案适合你,但我也会对你建议的方法的答案感兴趣 - 通过AR :: Associations :: Preloader定制的渴望加载听起来既酷又有用。 / p>