ruby-on-rails find_by_sql到返回activeRecords关系的对象

plupiseo  于 2023-03-09  发布在  Ruby
关注(0)|答案(3)|浏览(108)

我需要find_by_sql来返回一个ActiveRecord::Relation对象。这似乎是不可能的。然后我想用纯Ruby编写我的查询。
我有一个与自身有关系的表:

class Alarma < ApplicationRecord
  **belongs_to :root, class_name: "Alarma", foreign_key: "relacionada_id", optional: true
  has_many :children, class_name: "Alarma" , foreign_key: "relacionada_id"**

这是一张表:

create_table "alarmas", force: :cascade do |t|
    t.string   "sysname"
    t.string   "component"
    t.string   "details"
    t.integer  "estado_id"
    t.integer  "relacionada_id"
    t.datetime "created_at",     null: false
    t.datetime "updated_at",     null: false
    t.index ["estado_id"], name: "index_alarmas_on_estado_id", using: :btree
    t.index ["severity_id"], name: "index_alarmas_on_severity_id", using: :btree
  end

这是我用sql编写的查询:

Alarma.find_by_sql("SELECT a1.*  FROM alarmas a1 LEFT OUTER JOIN alarmas a2 ON a1.relacionada_id=a2.id 
WHERE a1.estado_id IN (1,2) OR a2.estado_id IN (1,2)")

报警可以是根,然后它有子报警,或者它可以处于以下状态:estado_id = 2,这意味着它是一个子警报,并且具有根警报(此警报是relacionada_id)。
示例:
根报警,关系标识=空,标识= 99子报警,关系标识= 99,标识= 112
我需要的是状态estado_id = 1或2的报警列表,以及其根处于状态estado id 1或2(及其状态)的报警列表
我可以使用find_by_sql让它与我自己的sql查询一起工作,但它返回一个数组,我需要一个ActiveRecord::Relation对象,因为我需要继续处理结果(因为我使用分页gem,order和page方法不处理数组)

wqsoz72f

wqsoz72f1#

参见Converting an array of objects to ActiveRecord::Relation
技巧是获取原始SQL语句结果的id,然后使用where执行第二个查询,这样它将返回ActiveRecord::Relation

as_array = Alarma.find_by_sql("SELECT a1.*  FROM alarmas a1 LEFT OUTER JOIN alarmas a2 ON a1.relacionada_id=a2.id WHERE a1.estado_id IN (1,2) OR a2.estado_id IN (1,2)")

as_relation = Alarma.where(id: as_array.map(&:id))
k75qkfdt

k75qkfdt2#

为了补充前面的答案,按照与原始SQL排序相同的顺序对关系进行排序,您将在模型上定义以下范围:

# NOTE: should be applied AFTER! all order statements
  scope :by_ordered_ids, -> (column_name, ordered_ids, type = 'INT') do
    ordered_ids_s = ordered_ids.join(',')
    joins(<<~SQL).order('y.index')
      JOIN (
        SELECT x.arr[x.index] AS id, x.index
          FROM (SELECT arr, GENERATE_SUBSCRIPTS(arr, 1) AS index
                  FROM (SELECT '{#{ordered_ids_s}}'::#{type}[]) T(arr)) x
      ) y ON #{table_name}.#{column_name} = y.id
    SQL
  end

...然后像这样使用:

as_relation = Alarma.where(id: as_array.map(&:id)).by_ordered_ids(:id_column, as_array.map(&:id))
kmpatx3s

kmpatx3s3#

您可以使用from()方法从子查询获取数据,以便它从临时表获取记录:

subquery = "SELECT a1.*  FROM alarmas a1 LEFT OUTER JOIN alarmas a2 ON a1.relacionada_id=a2.id WHERE a1.estado_id IN (1,2) OR a2.estado_id IN (1,2)"

records = Alarma.from("(#{subquery}) temp_table_alias") #=> ActiveRecord relation

records.to_sql #=> "SELECT * FROM (subquery) temp_table_alias"

相关问题