Import dữ liệu lớn từ file CSV
Import nhiều dữ liệu vào Rails ActiveRecord models có thể import từ các file JSON, CSV hoặc các loại file khác. Bài này mình sẽ nói về cách import nhiều dữ liệu từ file CSV. Importing từ file CSV vào trong Rails Cách xấu Ví dụ trong file CSV của mình có 10000 bản ghi require 'csv' csv = ...
Import nhiều dữ liệu vào Rails ActiveRecord models có thể import từ các file JSON, CSV hoặc các loại file khác. Bài này mình sẽ nói về cách import nhiều dữ liệu từ file CSV.
Importing từ file CSV vào trong Rails
Cách xấu Ví dụ trong file CSV của mình có 10000 bản ghi
require 'csv' csv = File.read('link/to/file.csv') CSV.parse(csv, headers: true).each do |row| Item.create(row.to_h) end
Cách này sẽ mất rất nhiều thời gian hoặc bị lỗi không import được dữ liệu vì:
- Load toàn bộ file CSV vào trong bộ nhớ
- Tạo trăm nghìn bản ghi một
Cách tốt hơn
CSV.foreach('link/to/file.csv', headers: true) do |row| Item.create(row.to_h) end
Cách này thực hiện từng hàng một thay vì load toàn bộ dữ liệu vào trong bộ nhớ nhưng nó vẫn chậm vì mình vẫn dùng cách vòng lặp từng hàng để tạo dữ liệu vào trong database. Cách tốt nhất Dùng active record import để import dữ liệu
items = [] CSV.foreach('link/to/file.csv', headers: true) do |row| items << Item.new(row.to_h) end Item.import(items)
Thay vì tạo từng bản ghi một mình sẽ thêm dữ liệu vào trong array và chỉ dùng duy nhất một query để import dữ liệu vào trong database
Import dữ liệu mới có quan hệ với dữ liệu cũ
Mình có rất nhiều bản ghi đang cần import và các dữ liệu đó nó có quan hệ với các bảng khác. Lấy ví dụ: có bảng List và bảng Item . Quan hệ giữa 2 bảng là List has_many :items Cách xấu
items = [] CSV.foreach('link/to/file.csv', headers: true) do |row| list = List.find_by(name: row[:name]) items << Item.new(list: list, title: row[:title]) end Item.import(items)
Mặc dù đã dùng active record import nhưng lại vòng lặp từng hàng đê tìm kiếm find_by trong bảng List Cách tốt nhất
lists_hash = List.pluck(:name, :id).to_h #// => {'list A' => 1, 'List B' => 2} items = [] CSV.foreach('link/to/file.csv', headers: true) do |row| list_id = lists_hash[row[:name]] items << Item.new(list_id: list_id, title: row[:title]) end Item.import(items)
Dùng một query đê lấy dữ liệu List name, id và đổi thành dạng hash. Trong vòng lặp sẽ không query để tìm bàn ghi của List nữa mà chỉ cần tìm id theo List name trong hash.