我正试图优化我的查询,以了解RAM的使用情况,而.includes(:ocr(中的一个表上有太多列(还有一个大列(。有没有一种方法可以让我仍然使用Rails的.includes((,但从includes((表中选择显式的.select((列?
我想排除使用N+1的情况,这是.joins((生成的。
即。
User.select(:email).includes(:ocr => select(:small_value_only))
您没有提供太多关于如何设置表的信息,所以我假设User为belongs_to :ocr
,User有一个ocr_id
列,ocr表的名称为ocr
。
应该使用joins
而不是includes
,这就是生成INNER JOIN查询并能够检索联接表的列的方式。最后,您可以使用AS
对列名进行别名,这样在您的模型中更容易检索:
users = User.joins(:ocr).select(:email, :ocr_id, '`ocr`.`small_value_only` AS `ocr_sma`')
users.each do |user|
user.email # Users.email for this record
user.ocr_sma # Joined Ocr.small_value_only for this record
end
(很明显,我把它别名为ocr_sma
,但你可以给它起你想要的名字(
如果您真的只想获取列数据而不是完整的ActiveRecord对象,那么应该使用pluck
而不是select
。加速这类大查询正是pluck
的初衷。pluck
只是返回一个数据数组:
values = User.includes(:ocr).pluck(:email, "ocr.small_value_only")
# SELECT "user"."email", ocr.small_value_only FROM "users"
# LEFT OUTER JOIN "ocrs" ON "ocr"."user_id" = "users"."id"
#
# [["email_1", 3], ["email_2", 7]] # 3 and 7 are the small values
如果您想要ActiveRecord对象,但希望跳过填充某些数据,则与上面相同,但用left_joins
替换includes
,用select
替换pluck
values = User.left_joins(:ocr).select(:email, "ocr.small_value_only")
# SELECT "user"."email", ocr.small_value_only FROM "users"
# LEFT OUTER JOIN "ocrs" ON "ocr"."user_id" = "users"."id"
#
# #<ActiveRecord::Relation [#<user id: 1>, #<user id: 2>]>
无论哪种方式,正如您所看到的,SELECT
语句都是相同的,并且只返回选定的数据列。
我不想说以下选项一定比Benj和Old Pro建议的要好,而是想提供更多的替代方案。
第一个使用了includes
方法,因为这正是OP所要求的。但是,由于默认情况下includes
将选择主模型(User
(的所有列,因此必须首先通过except方法删除该默认选择:
User.includes(:ocr).except(:select).select(:id, 'ocr.small_value_only')
当然,首先使用join
会更容易。
以下选项源于我不喜欢在AR查询中使用字符串,因为它往往依赖于AR实际上应该为您抽象的被调用模型的知识,即数据库表(ocr
(的名称。
去除这些知识的一个简单实现是从模型中获取表名:
User.joins(:ocr).select(:email, "`#{Ocr.table_name}`.`small_value_only`")
下一种选择依赖于merge来去除用于选择列的字符串:
User.joins(:ocr).merge(Ocr.select(:project_id)).select(:id)
但是,由于生成的sql没有引用small_value_only
的Ocr表,因此相当于写入:
User.joins(:ocr).select(:email, :small_value_only)
并且仅在CCD_ 23不存在于CCD_。
最后一种选择没有这个缺点,但要详细得多:
User.joins(:ocr).select(Ocr.select(:small_value_only).arel.projections).select(:id)
我认为您应该在User like:中创建一个新的关联
belongs_to :ocr_small_value_only, -> {select('ocr.id,ocr.small_value_only')}, class_name: 'ocr', foreign_key: 'ocr_id'
然后使用进行查询
User.select('ocr_id, email').includes(:ocr_small_value_only)