简体   繁体   English

如何使用 Rails 优化此查询

[英]How can I optimize this Query with Rails

I have an array of book ids and I need to iterate each and save the number of comments for each in a Hash (@book_comments) that will have the book_id as the key and the number of comments for that book as a value.我有一个书籍 id 数组,我需要迭代每个并将每个人的评论数量保存在一个哈希(@book_comments)中,该哈希将 book_id 作为键,并将该书的评论数量作为值。 Book has many comments, comment belong to books.书上有很多评论,评论属于书。

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

This will hit the DB with these two queries for the amount of ids I have on my array.这将通过这两个查询访问数据库,以获取我在阵列上的 ID 数量。

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

Surely there's a better way.肯定有更好的方法。 If you know, please teach me.知道的请教教我。 Thank you.谢谢你。

这应该得到你的哈希:

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

Use this code:使用此代码:

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, ...}

It does a single database query returning Book objects with only :id and :comments_count attributes filled out.它执行单个数据库查询,返回仅填写:id:comments_count属性的Book对象。 The result is then transformed using inject to a hash with book ids as keys and comments counts as values.然后使用inject将结果转换为以书籍 ID 作为键和评论作为值的散列。

If you're using Rails 4 you can do the following as well:如果您使用的是 Rails 4,您也可以执行以下操作:

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

It'll generate something like this:它会产生这样的东西:

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

Does this work for you?这对你有用吗? I'm not sure if I understood the question correctly.我不确定我是否正确理解了这个问题。 Let me know.让我知道。

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

Update更新

Is this closer to what you want?这是否更接近你想要的?

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

Try this, too也试试这个

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

You should use includes to eager load the comments then iterate over that query.您应该使用包含来预先加载评论,然后遍历该查询。

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM