matsuok’s diary

あくまでも個人的意見であり感想です

CSV ファイルをデータベースにロード(天候データ) Rubyで処理

データベースのCLICSVからのロードコマンドは提供されているので、そのままなら、開発は不要。
データ加工などある場合に有効

  • 気象庁からCSVにエクスポート
  • エデイタで読み込み、ヘッダー行は削除し漢字コードをutf8で保存
  • ヘッダーない場合は、行のカラム配列のハッシュ配列とならず、添え字でカラム値を参照
require 'csv'
require "date"
require 'sqlite3' # sqlite3読み込み

begin
  db = SQLite3::Database.new 'health.db'
rescue SQLite3::SQLException => e
  puts "open database return: "
  puts "#{e}\n"
end

table_name = "weather"
puts "$0:#{$0}"
ARGV.each_with_index do |arg, i|
  puts "ARGV[#{i}]:#{arg}"
  csvfile = ARGV[i]
  csv_data = CSV.read(csvfile)
  sum = 0.0
  cnt = 0
  max = -999.0
  min = 999.999
  fromdate = nil
  todate = nil
  csv_data.each do |data|
    if fromdate == nil
      fromdate = data[0]
    end
    todate = data[0]
    intro_msg = "#{data[0]}#{data[1]}平均気温(℃)、#{data[10]}天気概況(昼:06時~18時)です。\n"
    if data[1] == nil
      puts 'no data  ' + intro_msg
      next
    else
      sum = sum + data[1].to_f
      cnt = cnt + 1
      if max < data[1].to_f
        max = data[1].to_f
      end
      if min > data[1].to_f
        min = data[1].to_f
      end
      # 各行
      # 0  wdate text,         /* 年月日 */
      # 1  avgtemp real,       /* 平均気温(℃) */
      # 2  avgtempqi integer,  /* 平均気温(℃)品質情報 */
      # 3  avgtemphn integer,  /* 平均気温(℃)均質番号 */
      # 4  maxtemp real,       /* 最高気温(℃) */
      # 5  maxtempqi integer,  /* 最高気温(℃)品質情報 */
      # 6  maxtemphn integer,  /* 最高気温(℃)均質番号 */
      # 7  mintemp real,       /* 最低気温(℃) */
      # 8  mintempqi integer,  /* 最低気温(℃)品質情報 */
      # 9  mintemphn integer,  /* 最低気温(℃)均質番号 */
      # 10  Weatherreport text, /* 天気概況(昼:06時~18時) */
      # 11  weatherqi integer,  /* 天気概況(昼:06時~18時)品質情報 */
      # 12  Weatherhn integer   /* 天気概況(昼:06時~18時)均質番号 */
      sql =  "INSERT INTO " + table_name + " VALUES ("
      num_lines = 0
      j = 0

      data.each do |field|
        cell = field.to_s
        # 「,,,」のような行では、フィールド値が  nil になる。そのときは NULL
        if ( field.nil? )
          sql = sql +  "NULL"
        elsif ( field.empty? )
          # 元の CSV ファイルで,「空」になっているようなセル
          sql = sql +  "NULL"
        elsif ( j == 0 || j == 10 )
          # Y/M/D -> YY/MM/DD
          if  ( j == 0 )
            d = Date.parse(cell)
            cell = d.strftime("%Y/%m/%d")
          end
          # 文字列属性のときは「'」で囲む. gsub を使い、文字列中の全ての「'」は「''」に置き換える(これは SQL の流儀)

          sql = sql +  "'" + cell.gsub("'","''") + "'"
        elsif
          # 数値("INTEGER" または "REAL")のときは,文字列の先頭と末尾の空白文字を取り除く
          sql = sql +  cell.strip
        end
        if ( j < (data.size - 1) )
          # 最後のフィールドでないときは「,」で継続
          sql = sql +  ", "
        end
        j += 1

      end

      # 最後は「);」で終わる
      sql = sql +  ");"
      num_lines += 1
      # 1000 行ごとにコミット
      if num_lines == 1000
        db.execute("COMMIT;")
        puts "commit return\n"
        db.execute("BEGIN TRANSACTION;")
        puts "BEGIN TRANSACTION return\n"
        num_lines = 0
      end

      begin
        db.execute(sql)
      rescue SQLite3::SQLException => e
        puts "#{sql} database return:}"
        puts "#{e}\n"
      end
      puts "record:#{cnt.to_s} #{sql} return\n}"
    end
  end
  db.close
  puts "database close return\n}"
  puts 'start:' + fromdate + ' to:' + todate + "\n"
  total_msg = "合計件数:#{cnt} 平均気温(℃):#{(sum/cnt).round(3)} MAX平均気温(℃):#{max} MIN平均気温(℃):#{min}\n"
  puts total_msg
end