0

Thêm dữ liệu số lượng lớn với gem activerecord-import

1.Giới thiệu

Trong lập trình, chúng ta hẳn sẽ gặp các trường hợp cần insert một lượng lớn dữ liệu vào trong cơ sở dữ liệu, và trường hợp thường gặp nhất là đọc dữ liệu từ các file excel, csv... và thêm vào cơ sở dữ liệu. Và nếu thực hiện theo cách thông thường thì số câu lệnh SQL phải thực thi sẽ rất nhiều. Xét ví dụ dưới đây:

Ví dụ: Ở đây mình có một bảng Product gồm 2 trường name và price, mình sẽ khởi tạo 100 đối tượng và sau đó insert vào trong cơ sở dữ liệu

    products = []
    
    100.times do |i|
      products << Product.new(name: "Product #{i}", price: "1000")
    end

    products.each do |product|
      product.save
    end

Và đây là kết quả, quá trình xử lý ở model hết khoảng 14 giây, và có 100 câu query insert được thực thi để chèn dữ liệu vào trong database, nếu trong trường hợp này chúng ta có 1.000 hay 10.000... Product thì số lượng query sẽ rất lớn và thời gian thực thi sẽ rất lâu. Và may mắn thay, có một thư viện có thể giúp chúng ta trong việc này là gem activerecord-import

2.Cài đặt và sử dụng

2.1 Cài đặt

Cách cài đặt rất đơn giản, bạn chỉ cần thêm gem "activerecord-import" vào Gemfile và chạy lệnh bundle install để cài đặt. Sau khi cài đặt, gem sẽ tự tạo một class method trong class ActiveRecord::Base là import

Và đây là kết quả khi sử dụng gem activerecord-import, thời gian được rút ngắn rất đáng kể, chỉ còn khoảng 0,5s. Vì activerecord-import đã lấy dữ liệu và chỉ sử dụng 1 câu lệnh insert để chèn nhiều dữ liệu vào database

Đây là tính năng chính của activerecord-import là tạo ra số lượng câu insert nhỏ nhất , tránh vấn đề N+1 câu insert

2.2 Sử dụng

- Sử dụng ActiveRecord Models

    products = []
    
    100.times do |i|
      products << Product.new(name: "Product #{i}", price: "1000")
    end
    
    Product.import products

Với quan hệ association ta sử dụng option :recursive

    products = []
    
    100.times do |i|
      product = Product.new(name: "Product #{i}", price: "1000")
      product.product_order.build user_id: user.id
      products << product
    end
    
    Product.import products,  recursive: true

- Sử dụng mảng

    product_values = []
    columns = [ :name, :price ]
    
    100.times do |i|
      product_values << ["Product #{i}", "1000"]
    end
    
    Product.import columns, product_values

-Sử dụng hash

    product_values = []
    
    100.times do |i|
      product_values << { name: "Product #{i}", price: "1000" }
    end
    
    Product.import product_values

- Các giá trị trả về Sau khi method import được thực thi nó sẽ trả về một đối tượng ActiveRecord::Import::Result chứa các thuộc tính thể hiện kết quả của qúa trình import

  • failed_instances : Một mảng các đối tượng mà validate fails và không được insert vào database
  • num_inserts : Số lượng các câu lệnh insert trong quá trình import dữ liệu
  • ids : Một mảng chưa id của các record được thêm thành công (chỉ có ở db Postgres)

- Các tùy chọn

Key Tùy chọn Mặc định Mô tả
:validate true/false true Bật tắt validation
::validate_uniqueness true/false false Bật tắt các validation unique
:on_duplicate_key_ignore true/false false Nếu là true sẽ bỏ qua các record bị trùng khóa chính hoặc column unique
:on_duplicate_key_update :all, Array, Hash N/A Nếu bị trùng khóa chính hoặc column unique, sẽ ghi đè lên dữ liệu cũ
:recursive true/false false Sử dụng khi muốn import một object có quan hệ has_many associations (chỉ khả dụng ở db Postgres)
:timestamps true/false true Bật tắt timestamps cho các bảng ghi được import. Sử dụng trong trường hợp các bản ghi quá lớn ,để tránh trường hợp quá tải
:batch_size Integer Tổng số lượng records Chỉ định số lượng record sẽ được insert
:raise_error true/false false Bắn ra exception nếu có bất kỳ record nào không hợp lệ, có thể thay thế bằng method import!

3.Tham khảo


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí