在Ruby on Rails Activerecord中使用eager_load将可计算列添加到multi-table select子句

时间:2016-06-09 08:30:23

标签: ruby-on-rails rails-activerecord

我有一个包含很多连接的查询,我当时eager_load ServiceObject .joins([{service_days: :ou}, :address]) .eager_load(:address, :service_days) .where(ous: {id: OU.where(sector_code: 5)}) .select('SDO_CONTAINS(ous.service_area_shape, SDO_GEOMETRY(2001, 8307, sdo_point_type(addresses.lat, addresses.lng, NULL), NULL, NULL) ) AS in_zone') 某些关联。我需要计算一些值作为其中一个模型的属性。

所以,我正在尝试这段代码:

select

addresses中的SQL函数调用操作来自关联的ousin_zone表的数据。

我正在获取下一个SQL(因此我的eager_load列计算并在所有SELECT SDO_CONTAINS(ous.service_area_shape, SDO_GEOMETRY(2001, 8307, sdo_point_type(addresses.lat, addresses.lng, NULL), NULL, NULL) ) AS in_zone, "SERVICE_OBJECTS"."ID" AS t0_r0, "SERVICE_OBJECTS"."TYPE" AS t0_r1, <omitted for brevity> AS t2_r36 FROM "SERVICE_OBJECTS" INNER JOIN "SERVICE_DAYS" ON "SERVICE_DAYS"."SERVICE_OBJECT_ID" = "SERVICE_OBJECTS"."ID" INNER JOIN "OUS" ON "OUS"."ID" = "SERVICE_DAYS"."OU_ID" INNER JOIN "ADDRESSES" ON "ADDRESSES"."ID" = "SERVICE_OBJECTS"."ADDRESS_ID" WHERE "OUS"."ID" IN (SELECT "OUS"."ID" FROM "OUS" WHERE "OUS"."SECTOR_CODE" = :a1) [["sector_code", "5"]] ed模型的其他列之前作为第一列返回:

in_zone

但似乎in_zone无法从查询中使用的任何模型访问。

我需要计算ServiceObject作为"Thu Jun 09 2016 00:00:00 GMT+0200 (CEST)" 模型对象的属性,我该如何实现?

Ruby on Rails 4.2.6,Ruby 2.3.0,oracle_enhanced adapter 1.6.7,Oracle 12.1

2 个答案:

答案 0 :(得分:9)

我已成功复制了您的问题,结果是这是Rails中的known issue 。问题是当使用eager_load时,Rails会以t0_r0t0_r1的形式将所有预先加载的表的列映射到 table and column aliases 等等...(您可以在问题中粘贴的SQL中看到这些内容)。在执行此操作时,它只是忽略select中的自定义列,可能是因为它无法确定应将自定义列归因于哪个预先加载的表。令人遗憾的是,这个问题已经开放了2年多了......

尽管如此,我认为我找到了解决方法。似乎如果您不急于加载表格,但手动加入(使用joins),您可以包含它们(使用{ {1}})并且自定义列将作为返回,不会发生列别名。关键是你不能在includes子句中使用关联,但你必须自己指定连接。另请注意,您也必须手动选择主表中的所有列(请参阅选择中的joins)。

尝试以下方法:

service_objects.*

select中的计算仍然可以正常工作,因为相关表格ServiceObject .joins('INNER JOIN "SERVICE_DAYS" ON "SERVICE_DAYS"."SERVICE_OBJECT_ID" = "SERVICE_OBJECTS"."ID"') .joins('INNER JOIN "OUS" ON "OUS"."ID" = "SERVICE_DAYS"."OU_ID"') .joins('INNER JOIN "ADDRESSES" ON "ADDRESSES"."ID" = "SERVICE_OBJECTS"."ADDRESS_ID"') .includes(:service_days, :address) .where(ous: {id: OU.where(sector_code: 5)}) .select('service_objects.*, SDO_CONTAINS(ous.service_area_shape, SDO_GEOMETRY(2001, 8307, sdo_point_type(addresses.lat, addresses.lng, NULL), NULL, NULL) ) AS in_zone') 在一起,但不应存在列别名。

当然,这种方法意味着您将获得三个查询而不只是一个但除非您返回大量记录,否则join子句运行的以下两个查询应该非常快只需使用外键加载相关记录。

答案 1 :(得分:2)

那个猴子补丁帮助@Envek:

module ActiveRecord
  Base.send :attr_accessor, :_row_

  module Associations
    class JoinDependency
      JoinBase && class JoinPart
        def instantiate_with_row(row, *args)
          instantiate_without_row(row, *args).tap { |i| i._row_ = row }
        end; alias_method_chain :instantiate, :row
      end
    end
  end
end

然后可以这样做:

ServiceObject
  .joins([{service_days: :ou}, :address])
  .eager_load(:address, :service_days)
  .where(ous: {id: OU.where(sector_code: 5)})
  .select('SDO_CONTAINS(ous.service_area_shape, SDO_GEOMETRY(2001, 8307, sdo_point_type(addresses.lat, addresses.lng, NULL), NULL, NULL) ) AS in_zone')
  .first
  ._row_['in_zone']