2011-02-26 55 views
4

我的问题是Rails相关,但即使是一般的SQL答案也是有用的。从孙子表检索记录

我正在处理四个表:类别,书籍,categories_books和食谱。类别拥有并且属于许多书籍。书有很多食谱。

在Rails的代码翻译,我有:

class Category < ActiveRecord::Base 
    has_and_belongs_to_many :books 
end 

class Book < ActiveRecord::Base 
    has_and_belongs_to_many :categories 
    has_many :recipes 
end 

class Recipe < ActiveRecord::Base 
    belongs_to :book 
end 

我试图检索包含在属于某一类图书的所有配方。

我知道如何做到这一点与许多查询,但不是与一个单一的查询。由于许多疑问,我会做:

recipes = []  
books = @category.books 
books.each do |book| 
    recipes << book.recipes.flatten 
end 

我不喜欢它,因为它需要N + 1个查询。我可能需要一个连接一次完成,但我不确定ActiveRecord或SQL中的语法。我正在使用MySQL。

回答

1

不能帮助Ruby。一个普通的SQL答案(做出关于列名的一些假设)是:

SELECT Recipe.* 
FROM Recipe 
INNER JOIN Book ON Recipe.Book = Book.Id 
INNER JOIN Category ON Book.Category = Category.Id 
WHERE Category.Id = ? 

编辑增加查询的另一个版本,以配合小指大脑的有关表和列的新信息。像这样?

SELECT Recipe.* 
FROM Recipe 
INNER JOIN Categories_Books ON Categories_Books.book_id = Recipe.book_id 
WHERE Categories_Books.category_id = ? 
+0

有一个(非唯一)索引感谢Xoltar。不幸的是,这并不适合我。图书没有category_id,类别没有book_id。外键存储在Categories_Books表中。我试图替换第4行中的内容,将Book.Category也更改为Books.category_id。因此,我在“where子句”错误中收到“未知列'categories.id'。 :( – 2011-02-26 20:41:46

+0

@Pinky Brain:如果您要查询按类别id进行过滤(而不是按类别名称过滤),那么我认为您不需要将'Categories'或'Categories_Books'加入到这个查询,在Xoltar的解决方案中删除'INNER JOIN Category'行,并用'Books.category_id = ...'代替WHERE条件。 – 2011-02-26 21:13:01

+0

好的,增加了另一个查询版本,可能更接近你需要的您描述的架构。 – Xoltar 2011-02-27 00:02:52

0

假设下面的表结构...

category(
    category_id primary key 
) 

book(
    book_id primary key 
) 

recipes(
    recipe_id 
    ,book_id 
    ,primary key(recipe_id) 
) 

categories_books(
    category_id 
    ,book_id 
    ,primary key(category_id, book_id) 
) 

...你可以得到你想要用下面的查询,它返回从指定类别中的所有书籍都receipes什么。

select ... 
    from categories_books c 
    join book    b using(book_id) 
    join recipes   r using(book_id) 
where c.category_id = ?; 

请注意,您可以删除加入到book如果不从表中需要的任何数据,因为你可以直接从categories_books通过book_id加入到recipes。此外,请确保recipes.book_id