40

子供を持つ親を見つけるためのいくつかの使用可能な例を含む1つの回答が見つかりましたが、同じものは子供のいない親を見つけるためには使用できません(おそらく、結合によって除外されるため)。havingn

scope :with_children, joins(:children).group("child_join_table.parent_id").having("count(child_join_table.parent_id) > 0")

誰かが私を正しい方向に向けることができますか?

4

3 に答える 3

77

Rails 6.1 の更新

ここで説明されているように、新しい Rails バージョンではこれが簡単になります。

.where.missing(:children)

古いバージョンについては、以下を参照してください。

レール 3 & 4

scope :without_children, includes(:children).where(:children => { :id => nil })

ここでの大きな違いは、joinsになることincludesです。インクルードはすべてのリレーションをロードします。それらが存在する場合、結合は関連付けられたオブジェクトのみをロードし、リレーションのないオブジェクトを無視します。

実際にscope :with_children, joins(:children)は、少なくとも 1 つの子を持つ Parent を返すのに十分なはずです。やってみて!


レール5

以下の@Ansonの回答を参照してください


宝石activerecord_where_assoc

gemはactiverecord_where_assocRails 4.1 から 6.0 までこれを行うことができます。

scope :without_children, where_assoc_not_exists(:children)

自己参照関係はシームレスに処理されます。

joinsこれにより、クエリが単一のレコードに対して複数の行を返すなどの問題も回避されます。


@MauroDias が指摘したように、それが親と子の間の自己参照関係である場合、上記のコードは機能しません。

ちょっとした調査で、私はそれを行う方法を見つけました:

次のモデルを検討してください。

class Item < ActiveRecord::Base
  has_many :children, :class_name => 'Item', :foreign_key => 'parent_id'

子供のいないすべてのアイテムを返品する方法:

Item.includes(:children).where(children_items: { id: nil })

どうやってそのchildren_itemsテーブルを見つけたのですか?

Item.joins(:children)次の SQL を生成します。

SELECT "items".* 
FROM "items" 
 INNER JOIN "items" "children_items" 
 ON "children_items"."parent_id" = "items"."id"

したがって、自己参照の場合に JOIN が必要な場合、Rails はテーブルを使用すると推測しました。


同様の質問:

于 2013-08-06T13:51:56.433 に答える
31

@MrYoshiji には確かな Rails 4 の回答がありますが、Rails 5 を使用してここに来る人には、より多くのオプションがあります。

Rails 5 の使用:

Rails 5 以降では、left_outer_joinsを使用して関連付けの読み込みを回避することもできます。プルリクエスト#12071で導入されました。

scope :without_children, -> { left_outer_joins(:children).where(children: { id: nil }) }

子供を持つ親にとっては、Yoshiji 氏の Rails 4 ソリューションが引き続き使用されます。

scope :with_children, -> { joins(:children).uniq }
于 2016-11-09T16:04:03.133 に答える