如何使用Rails优化此查询

时间:2016-07-08 22:58:55

标签: mysql ruby-on-rails ruby activerecord

我有一个book id数组,我需要迭代每个并在Hash(@book_comments)中保存每个注释的数量,这些注释将book_id作为键,并将该书的注释数作为值。书中有很多评论,评论属于书籍。

@book_ids.map {|id| @book_comments[id] = Book.find(id).comments.size}

这将使用这两个查询命中数据库,查看我在数组上的ID数量。

SELECT `books`.* FROM `books` WHERE `books`.`id` = ? LIMIT 1
SELECT COUNT(*) FROM `comments` WHERE `comments`.`book_id` = ?

当然有更好的方法。 如果你知道,请教我。 谢谢。

5 个答案:

答案 0 :(得分:5)

这应该得到你的哈希:

@book_comments = Comment.where(book_id: @book_ids).group(:book_id).count

答案 1 :(得分:3)

使用此代码:

comment_counts_by_book = Book.select('books.id, count(comments.id) as comments_count').
    joins('left outer join comments on comments.book_id = books.id').
    group('books.id').inject({}) { |h, book| h[book.id] = book.comments_count; h }

comment_counts_by_book
# => {1=>2, 2=>5, ...}

它执行单个数据库查询,返回仅填充Book:id属性的:comments_count个对象。然后使用inject将结果转换为带有book id的哈希作为键,并将注释计为值。

答案 2 :(得分:1)

如果您正在使用Rails 4,您也可以执行以下操作:

Comment.eager_load(:book).group(:book).count

它会生成如下内容:

=> #{1=>2, 2=>4, 3=>9}

答案 3 :(得分:0)

这对你有用吗?我不确定我是否正确理解了这个问题。让我知道。

Book.find(@book_ids).map(&:comments).size

更新

这更接近你想要的吗?

@book_ids.map do |id|
  {
    id => Book.find(id).map(&:comments).size
  }
end

试试这个

{}.tap do |hash|
  @book_ids.each do |book_id|
    hash[book_id] = Book.find(book_id).map(&:comments).size
  end
end

答案 4 :(得分:0)

您应该使用includes来急切加载注释,然后迭代该查询。

books = Book.where(id: @book_ids).includes(:comments)

books.each do |book|
  @book_comments[book.id] = book.comments.length
end