1

私は、コードに見苦しいSQLをたくさん含まずに、Railsの魔法を使用して、かなり複雑なクエリになると思うものを実現しようとしています。

私のデータベースはかなり特殊な生物医学モデルを扱っているので、以下をより現実的なシナリオに変換します。

私はモデルブックを持っています

has_many:chapters

とその章

所属する:book

たとえば、チャプターにはname属性があり、名前は序文、紹介、付録にすることができ、ブックには序文という名前のチャプターと紹介という名前のチャプターを含めることができますが、付録という名前のチャプターはありません。実際、これらの任意の組み合わせ

序文と紹介の両方の章があるすべての本を探しています。

現在、私は次のようにnamed_scopeを持っています

Book
  named_scope :has_chapter?, lambda { |chapter_names|
    condition_string_array = []
    chapter_names.size.times{condition_string_array << "chapters.name = ?"}
    condition_string = condition_string_array.join(" OR ")
    {:joins => [:chapters]  , :conditions => [condition_string, * chapter_names]}
  }

私が本と呼んだら。has_chapter?["序文"、 "紹介"]これにより、序文または紹介という名前の章があるすべての本が見つかります。両方の章の序文と紹介の両方を分離するのと同じようなことをどのように行うことができますか?

私はSQLにあまり詳しくないので、どのような種類の結合が必要であり、これが名前付きスコープで実現できるかどうかはよくわかりません。

どうもありがとう

アンソニー

4

3 に答える 3

8

序文と紹介の両方の章があるすべての本を探しています。

私はpostgresではなくmysqlを使用していますが、postgresはサブクエリをサポートしていると思いますか?次のようなことを試すことができます:

class Book

  named_scope :has_preface, :conditions => "books.id IN (SELECT book_id FROM chapters WHERE chapters.name = 'preface')"
  named_scope :has_introduction, :conditions => "books.id IN (SELECT book_id FROM chapters WHERE chapters.name = 'introduction')"

end

その後:

Book.has_preface.has_introduction
于 2009-07-11T15:37:15.287 に答える
2

私はコードの凝縮が好きです

condition_string = Array.new(chapter_names.size, "chapters.name=?").join(" AND ")

これは、「OR」を使用した結合では実際に機能します。ただし、これは、結合の1行のchapters.nameが、たとえば「序文」と「紹介」の両方である必要があることを意味するため、機能しません。

私のオリジナルの「OR」参加を行うさらにきちんとした方法は

 named_scope :has_chapter?, lambda { | chapter_names |
   {:joins => [: chapters]  , :conditions => { :chapters => {:name => chapter_names}}}
 }

Duplex on Railsフォーラムに感謝します(リンクを投稿)DUPLEXがこれを提案しましたが、私の元の問題を達成するために

named_scope :has_chapters, lambda { |chapter_names|
  {:joins => :chapters  , :conditions => { :chapters => {:name => chapter_names}},
   :select => "books.*, COUNT(chapters.id) AS c_count",
   :group => "books.id", :having => "c_count = #{chapter_names.is_a?(Array) ? chapter_names.size : 1}" }
}

これは、SQLの一部のフレーバーでは機能する可能性がありますが、PostgreSQLでは機能しません。私は以下を使わなければなりませんでした

named_scope : has_chapter?, lambda { | chapter_names |
  {:joins => :chapters  , :conditions => { :chapters => {:name => chapter_names}},
   :select => "books.* , COUNT(chapters.id)",
   :group => Book.column_names.collect{|column_name| "books.#{column_name}"}.join(","), :having => "COUNT(chapters.id) = #{chapter_names.is_a?(Array) ? chapter_names.size : 1}" }
}

コード

Book.column_names.collect{|column_name| "books.#{column_name}"}.join(",")

PostgreSQLでは、books。*ですべての列を選択してから、books。*でGROUP BYを選択することはできないため、が必要です。各列には個別に名前を付ける必要があります:(

于 2009-07-07T09:03:21.023 に答える
1

序文と紹介の両方の章があるすべての本を探しています。

現在、私は次のようにnamed_scopeを持っています

これは、named_scopeなしで実行できます。

class Book < ActiveRecord::Base
  has_many :chapters

  def self.has_chapter?(chapter_names)
    Chapter.find_all_by_name(chapter_names, :include => [:book]).map(&:book)
  end
end
于 2009-07-06T20:06:10.273 に答える