[RUBY] Exportieren (Ausgeben) einer großen Menge von CSVs, z. B. Protokollinformationen in der WEB-Anwendung, mit der Rails-Anwendung

Die Ausgabedaten sind zu groß und benötigen Zeit

Ich habe eine Funktion zum Ausgeben von Protokollinformationen aus der Datenbank erstellt, da ich Protokollinformationen vom Client analysieren möchte.
Aufgrund der Ausgabe des Protokolls nach dem Start des eigentlichen Vorgangs dauerte die Ausgabe des CSV lange, wenn das Protokoll etwa 1 oder 2 Tage alt war. Es dauert jedoch zu lange, bis der gesamte Zeitraum nützlich ist. Ich bin in eine solche Situation geraten. Als ich es tatsächlich ausprobierte, wurde es auch nach 15 Minuten nicht ausgegeben und befand sich im Status "Ich kann dies nicht sicher verwenden ...". Daher habe ich versucht, das Protokoll auszugeben, indem ich direkt auf SQL geklickt habe.

Die bis dahin verwendete Methode

[Lassen Sie uns DB-Daten im CSV-Format mit Rails | ausgeben Ich habe die allgemeine CSV-Ausgabe mit der CSV-Bibliothek wie folgt ausgegeben. (Ist das das? library csv (Ruby 2.7.0 Referenzhandbuch)
Dieses Mal verwende ich die CSV-Bibliothek, um Abfragen mit unformatierten SQL- und Ausgabeprotokollen auszugeben, aber die Verwendung unterscheidet sich geringfügig.
Die Methode to_sql wird verwendet, um das von ActiveRecord ausgegebene SQL in Raw-SQL zu konvertieren und auszugeben.

app/services/large_csv_exporter_service.rb

class LargeCsvExporterService
  require 'csv'

  attr_accessor :table, :column, :host, :username, :password, :database

  def initialize(table, column, host, username, password, database)
    @table = table
    @column = column
    @host = host
    @username = username
    @password = password
    @database = database
  end

  def set_file_name(file_name)
    @csv_file_name = file_name
  end

  def set_query(query)
    results = @client.query(query)
    results
  end

  def write_csv(results)
    # File.write(@csv_file_name, encoding: Encoding::SJIS) unless File.exist? @csv_file_name
    CSV.open("./tmp/csv_export/#{@csv_file_name}", "w:sjis") do |csv|
      csv << results.fields
      results.each do |row|
        csv << row.values
      end
    end
  end

  def export_csv_file
    filepath = "./tmp/csv_export/#{@csv_file_name}"
    stat = File::stat(filepath)
    return filepath, stat, @csv_file_name
  end

  def delete_created_csv_file
    if File.exist?("./tmp/csv_export/#{@csv_file_name}")
      File.delete("./tmp/csv_export/#{@csv_file_name}")
    end
  end

  def set_client
    # cache_rows:Falsch, um die Verwendung von Speicher zu vermeiden
    @client = Mysql2::Client.new(
      host: host,
      username: username,
      password: password,
      database: database,
      stream: true,
      cache_rows: false,
    )
  end
end

Ich verwende den Dienst in einem Controller. Von der Mitte "csv_export = LargeCsvExporterService.new (" access_logs "," * ", ENV [" DB_D_HOST "], ENV [" DB_D_USERNAME "]," ", ENV [" DB_D_NAME "]) "ENV [" DB_D_HOST "]" bezieht sich auf den DB-Namen oder Benutzernamen von .env (dotenv).
Ich kann keinen hübschen Code schreiben, also schauen Sie bitte genau hin.

app/controllers/admins/access_logs_controller.rb

    def export_csv(params_q)
      @q = AccessLog.order(id: :asc).ransack(params_q)
      user_type_param = params[:q][:user_type].empty? ? ["user", "admin", "supplier"] : params[:q][:user_type].split(':')[1]
      methoda_type_param = params[:q][:method_type] == "ALL_TYPE" ? AccessLog.distinct.pluck(:method_type).compact : params[:q][:method_type]
      access_dates = AccessLog.order(access_date: :asc).to_a
      from_time = params[:q][:created_at_gteq].empty? ? access_dates.first.access_date : params[:q][:created_at_gteq].to_time
      to_time = params[:q][:created_at_lt].empty? ? access_dates.last.access_date : params[:q][:created_at_lt].to_time

      sql_query = AccessLog.where(user_type: user_type_param).where(method_type: methoda_type_param).where(created_at: [from_time..to_time]).to_sql

      csv_export = LargeCsvExporterService.new("access_logs", "*", ENV["DB_D_HOST"], ENV["DB_D_USERNAME"], "", ENV["DB_D_NAME"])
      csv_export.set_file_name("AccessLog_#{Time.zone.now.strftime("%Y%m%d%S")}.csv")
      csv_export.set_client
      # results = csv_export.set_query("SELECT * FROM access_logs WHERE created_at BETWEEN '#{from_time}' AND '#{to_time}' AND method_type = '#{methoda_type_param}' AND user_type = '#{user_type_param}'" )
      results = csv_export.set_query(sql_query)
      csv_export.write_csv(results)
      filepath, stat, csv_file_name = csv_export.export_csv_file
      send_result = send_file(filepath, :filename => csv_file_name, :length => stat.size)
      # csv_export.delete_created_csv_file
    end

Die SQL-Abfrage gibt so etwas in einer Zeile aus

SELECT `access_logs`.* 
FROM   `access_logs` 
WHERE  `access_logs`.`user_type` = 'user' 
       AND `access_logs`.`method_type` IN ( 'GET', 'POST' ) 
       AND `access_logs`.`created_at` BETWEEN 
           '2020-08-25 00:00:00' AND '2020-09-27 23:55:35' 

Nun, so habe ich die Zeit nicht wirklich gemessen, aber jetzt kann ich ungefähr 100.000 Dateien in ungefähr 10 Sekunden ausgeben.

Recommended Posts

Exportieren (Ausgeben) einer großen Menge von CSVs, z. B. Protokollinformationen in der WEB-Anwendung, mit der Rails-Anwendung
Etwa der Ablauf der Entwicklung von Webanwendungen mit Rails.
Erstellen Sie mit der Datei Ruby on Rails seeds.rb eine große Anzahl von Datensätzen mit einem einzigen Befehl
Die erste WEB-Anwendung mit Spring Boot-Making a Pomodoro Timer-
Überprüfen Sie die Funktion von zwei Rollen mit einer Chat-Anwendung
Ein Hinweis zum Seed-Feature von Ruby on Rails
So beheben Sie das Problem, dass beim Stoppen der Webanwendung kein Protokollierungsprotokoll ausgegeben wird
Stellen Sie die Sensorinformationen von Raspberry Pi in Java grafisch dar und überprüfen Sie sie mit einem Webbrowser
Die Geschichte des Refactorings mit einem selbstgemachten Helfer zum ersten Mal in einer Rails-App
Vergleich der WEB-Anwendungsentwicklung mit Rails und Java Servlet + JSP
[Rails / Heroku / MySQL] So setzen Sie die Datenbank der Rails-App auf Heroku zurück
Eine Geschichte, die mit der Einführung von Web Apple Pay zu kämpfen hatte
Ich habe ein Tool erstellt, um den Unterschied zwischen CSV-Dateien auszugeben
[Rails] Implementierung der CSV-Exportfunktion
[Ruby on Rails] CSV-Ausgabefunktion
Erstellen Sie eine Webanwendung mit Javalin
[Java] Stellen Sie eine mit Eclipse + Maven + Ontology mit Heroku erstellte Webanwendung bereit
Umfang des Versuchs, eine Java-Webanwendung unter Windows Server 2016 zu erstellen