1

我正在尝试使用 Rails 的魔力来实现我认为相当复杂的查询,而代码中没有很多丑陋的 SQL。

由于我的数据库正在处理相当专业的生物医学模型,因此我将以下内容转换为更真实的场景。

我有一本样板书

has_many:章节

和那章

属于_to:书

例如,章节有一个名称属性,名称可以是序言、介绍和附录,一本书可以有一个名为序言的章节和一个名为介绍的章节,但没有名为附录的章节。事实上,这些的任何组合

我正在寻找所有既有章节都命名为序言和介绍的书籍。

目前我有一个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]}
  }

如果我打电话给书。有章?["preface", "introduction"] 这将为我找到所有具有名为序言或介绍的章节的书籍。我怎么能做类似的事情,发现我隔离章节序言和介绍?

我对 SQL 不是很熟悉,所以不太确定需要什么样的连接,以及这是否可以在命名范围内实现。

非常感谢

安东尼

4

3 回答 3

8

我正在寻找所有既有章节都命名为序言和介绍的书籍。

我使用 mysql,而不是 postgres,但我认为 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”的连接。但是 AND 连接不起作用,因为这意味着连接的单行中的 chapters.name 必须同时是“前言”和“简介”。

做我原来的“或”加入的一种更简洁的方法是

 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 中,您不能选择所有包含书籍的列。* 然后 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 回答