我正在尝试使用Rails的魔力来实现我认为相当复杂的查询,而不会在代码中看到很多丑陋的SQL。
由于我的数据库处理的是相当专业的生物医学模型,我将把以下内容翻译成更真实的场景。
我有一本模型书
has_many:章节
和
的章节belongs_to:book
比如说章节有一个名称属性,名称可以是前言,介绍和附录,一本书可以有一个名为前言的章节和一个名为引言的章节,但没有章节命名附录。实际上是这些的任意组合
我希望找到所有包含章节命名前言和介绍的书籍。
目前我有一个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]}
}
如果我打电话给Book。 has_chapter? [“前言”,“介绍”]这将找到我所有的书籍,其中有一章名为前言或介绍。我怎么能做类似的事情才能找到两者章节序言和介绍?
我不熟悉SQL,所以不太确定需要什么样的连接以及是否可以在命名范围内实现。
非常感谢
安东尼
答案 0 :(得分: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
答案 1 :(得分:2)
我喜欢将代码压缩到
condition_string = Array.new(chapter_names.size, "chapters.name=?").join(" AND ")
这确实适用于带有“OR”的连接。但是AND连接将不起作用,因为这意味着连接的单行中的chapters.name必须是“前言”和“引言”,例如
进行原始“OR”加入的更简洁的方法是
named_scope :has_chapter?, lambda { | chapter_names |
{:joins => [: chapters] , :conditions => { :chapters => {:name => chapter_names}}}
}
感谢Rails论坛上的Duplex(Post link) 为了实现我原来的问题,虽然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书籍。*每列必须单独命名:(
答案 2 :(得分: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