簡體   English   中英

ActiveRecord:LEFT OUTER自聯接,聯接表上有條件

[英]ActiveRecord : LEFT OUTER self-JOIN with conditions on joined table

我有一個帶有自反關聯的ActiveRecord類( 注意:出於非公開的原因,類名被刪除 ):

   class Thing < ActiveRecord::Base

     belongs_to :parent, 
                class_name: 'Thing'

     has_many :children, 
              class_name:  'Thing', 
              foreign_key: :parent_id

     scope :children, ->{ where(arel_table[:parent_id].not_eq(nil)) }

   end

我想讓所有孩子,在外部與他們的父母保持聯系,並在父母存在的情況下對父母附加條件,同時仍然渴望加載父母。

通常在這種情況下,我會執行以下操作:

 Thing.includes(:other_thing).merge(OtherThing.some_scope)

但是在這種情況下是不可能的:因為這是自連接,所以如果我合並Thing的作用域,則條件將適用於孩子的東西,而不適用於父母的東西。

我試圖在原始SQL中執行此操作:

  scope :some_scope, ->{
    children.joins(<<-SQL).references(:parent)
      LEFT OUTER JOIN things AS parents
        ON  things.parent_id = parents.id 
        AND parents.some_field IN ('foo', 'bar')
    SQL
  }

該查詢是可以的,但是似乎父級並沒有急於加載(嘗試在此作用域上map(&:parent)時有n + 1個查詢)。

就目前而言,我或多或少的工作解決方案是使用子查詢(拔出與父項條件匹配的ID,然后parent_id IN此集合中選擇具有parent_id IN子項),但我想知道是否存在更合適的/優雅的。

我還沒有找到一種使用merge (似乎堅持使用table_name模型),但是您可以使用includes並圍繞期望的別名構建條件-您需要根據以下內容修改此別名您自己的命名:

User.includes(:parent).
      where('parents_users.some_field IN (?)', ['foo', 'bar']).
      references(:parents_users)

但是,您可能需要注意NULL父ID,因為該條件將添加到WHERE子句中,而不是添加到LEFT JOIN本身中:

User.includes(:parent).
      where('parents_users.some_field IN (?) OR parents_users.id IS NULL', ['foo', 'bar']).
      references(:parents_users)

這應該正確執行所有急切的加載。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM