Query khi count record dữ liệu
Bài đăng này đã không được cập nhật trong 8 năm
Trong Rails đã hỗ trợ một method includes dùng để hổ trợ việc giảm query trong truy vấn cơ sở dữ liệu quan hệ. Như vậy ở đây mình đặt ra một bài toán như sau:
Mình có table categories has_many với table posts và mình muốn lấy list categories và count các bài post tương ứng của category đó thì mình làm như sau:
# Controller
class CategoryController < ApplicationController
def index
categories = Category.all
end
end
# View
<% @categories.each do |category| %>
<h1><%= category.name %>(<%= category.posts.count %>)<h1>
<% end %>
Nhìn vào console bạn sẽ thấy như sau:
Category Load (0.1ms) SELECT categories.* FROM categories
(0.2ms) SELECT COUNT() FROM posts WHERE posts.category_id = 2
(0.3ms) SELECT COUNT() FROM posts WHERE posts.category_id = 3
(0.2ms) SELECT COUNT(*) FROM posts WHERE posts.category_id = 4
3 câu lệnh SELECT count được thực hiện. Điều này sẽ ảnh hưởng đến tốc độ truy vấn dữ liệu khi hệ thống dữ liệu lớn. Để giải quyết vấn đề này mình sử dụng cách sau:
- Tạo một câu query
Mình tạo một cái scope với dòng query như sau:
# Model
class Category < ActiveRecord::Base
has_many :posts
scope :with_count_posts, -> {joins(:posts).select("categories.* ,Count(posts.id) AS posts_count").group("categories.id")}
end
# Controller
class CategoryController < ApplicationController
def index
@categories = Category.with_count_posts
end
end
# View
<% @categories.each do |category| %>
<h1><%= category.name %>(<%= category.posts_count %>)<h1>
<% end %>
Nhìn vào console bạn sẽ thấy sự khác biệt
Category Load (0.1ms) SELECT categories.* ,Count(posts.id) AS posts_count FROM categories INNER JOIN posts ON posts.category_id = categories.id GROUP BY categories.id
Lưu ý
scope ở trên sẽ không lấy ra những category không có record posts nào. Nếu muốn lấy hết thì bạn có thể tạo một scope như dưới đây.
scope :with_count_posts, -> {joins("LEFT JOIN posts ON categories.id = posts.category_id").select("categories.* ,Count(posts.id) AS posts_count").group("categories.id")}
Ngoài ra các bạn có thể dùng một vài gem sau
Chúc các bạn thành công.
All rights reserved