J'ai créé une fonction pour générer des informations de journal à partir de la base de données, car je souhaite analyser les informations de journal du client.
En raison de la sortie du journal après le début de l'opération réelle, il a fallu beaucoup de temps pour sortir le csv si le journal était d'environ 1 ou 2 jours. Cependant, il faut trop de temps pour que toute la période soit utile. Je suis tombé dans une telle situation. Quand je l'ai essayé, il n'était pas sorti même après 15 minutes, et il était dans l'état "Je ne peux pas l'utiliser à coup sûr ...", j'ai donc essayé de sortir le journal en appuyant directement sur SQL.
Sortons les données DB au format CSV avec Rails | Notes diverses qui n'ont jamais été vues | note
J'avais l'habitude de sortir la sortie csv commune en utilisant la bibliothèque CSV comme celle-ci. (C'est ça?
bibliothèque csv (Manuel de référence Ruby 2.7.0)
)
Cette fois, j'utilise la bibliothèque CSV pour émettre des requêtes avec du SQL brut et des journaux de sortie, mais l'utilisation est légèrement différente.
La méthode to_sql
est utilisée pour convertir le SQL émis par ActiveRecord en SQL brut et le sortir.
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:Faux pour éviter d'utiliser la mémoire
@client = Mysql2::Client.new(
host: host,
username: username,
password: password,
database: database,
stream: true,
cache_rows: false,
)
end
end
J'utilise le service dans un contrôleur. A partir du milieu csv_export = LargeCsvExporterService.new (" access_logs "," * ", ENV [" DB_D_HOST "], ENV [" DB_D_USERNAME "]," ", ENV [" DB_D_NAME "])
. ʻENV ["DB_D_HOST"] `fait référence au nom de base de données ou au nom d'utilisateur de .env (dotenv).
Je ne peux pas écrire un joli code, alors regardez bien.
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
La requête SQL renvoie quelque chose comme ça sur une seule ligne
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'
Eh bien, comme ça, je n'ai pas réellement mesuré le temps, mais maintenant je peux produire environ 100 000 fichiers en environ 10 secondes.
Recommended Posts