ruby-on-rails 使用参数化查询从原始SQL查询返回ActiveRecord关系以加快响应速度

nnvyjq4y  于 7个月前  发布在  Ruby
关注(0)|答案(1)|浏览(93)

我有一个名为VideoEventBundle的Rails模型,它包含单独的事件。这通过外键与VideoMeasurement模型相关联,而VideoMeasurement模型本身是Measurement的可能多态子模型之一。
SQL架构:

CREATE TABLE public.video_event_bundles (
    id bigint NOT NULL,
    video_measurement_id bigint,
    events jsonb DEFAULT '[]'::jsonb
);

CREATE TABLE public.video_measurements (
    id bigint NOT NULL,
);

CREATE TABLE public.measurements (
    id bigint NOT NULL,
    customer_id bigint NOT NULL,
    actable_type character varying,
    actable_id bigint
);

字符串
现在,在一个主要返回测量值的API上下文中,我还想导出属于用户选择/过滤的测量值的所有视频事件包。为此,我有一个通用方法,根据用户的过滤器将@measurements设置为ActiveRecord关系。例如:

@measurements = Measurement.where(customer_id: params[:customer_id])


为了获得实际的事件包,我使用了这个查询:

VideoEventBundle.find_by_sql([
      "SELECT video_event_bundles.*
        FROM video_event_bundles
          JOIN video_measurements ON video_event_bundles.video_measurement_id = video_measurements.id
          JOIN measurements ON video_measurements.id = measurements.actable_id
        WHERE measurements.actable_type = 'VideoMeasurement'
        AND measurements.id IN (?)
      ",
      @measurements.select(:id)
    ])


问题是,这返回一个对象数组,而不是一个ActiveRecord关系。问题是,在未过滤的测量值的情况下,该数组将是巨大的。由于API是分页的,我需要能够将limit/offset传递给该方法,因此,我必须实际返回一个ActiveRecord关系。
现在,我已经看到了多个与从原始SQL查询返回相关的问题:

但是我无法使用(?)参数来实际替换ID:

sql = <<-SQL
      SELECT video_event_bundles.*
        FROM video_event_bundles
          JOIN video_measurements ON video_event_bundles.video_measurement_id = video_measurements.id
          JOIN measurements ON video_measurements.id = measurements.actable_id
        AND measurements.id IN (?)
    SQL

    VideoEventBundle.select("*").from("(#{sql}) AS video_event_bundles", @measurements.select(:id))


这会引发错误:

ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR:  syntax error at or near ")"
LINE 5:         AND measurements.id IN (?)


我想我不能通过用@measurements.join(…)开始查询来解决这个问题?

8fq7wneg

8fq7wneg1#

应该可以仅使用:用途:

VideoEventBundle
  .joins(video_measurement: :measurement)
  .where(measurement: { id: @measurements.select(:id) })

字符串
如果没有,我们绝对可以使用AR的内置机制和一点Arel来完成你所需要的SQL:

measurement_table = Measurement.arel_table
vm_table = VideoMeasurement.arel_table
measurement_join = measurement_table.join(vm_table)
   .on(vm_table[:id].eq(measurement_table[:actable_id])
         .and(measurement_table[:actable_type].eq('VideoMeasurement')))
VideoEventBundle
  .joins(:video_measurement)
  .joins(measurement_join)
  .where(measurements: {id: @measurements.select(:id)})


这将导致

SELECT
    video_event_bundles.*
FROM 
    video_event_bundles
    INNER JOIN video_measurements 
      ON video_event_bundles.video_measurement_id = video_measurements.id
    INNER JOIN measurements 
      ON video_measurements.id = measurements.actable_id 
         AND measurements.actable_type = 'VideoMeasurement'
WHERE 
   measurements.id IN (
     SELECT 
        measurements.id
     FROM 
        measurements
     WHERE
        measurements.customer_id = 1234
   )

相关问题