我们有一个相当简单的查询,可以与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。
答案 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>