sql >> データベース >  >> RDS >> Mysql

RからSQLにデータフレームを挿入する効率的な方法

    TL; DR: LOAD DATA INFILE 複数のINSERTよりも1桁高速です ステートメントは、それ自体が単一のINSERTよりも1桁高速です。 ステートメント。

    RからMysqlにデータをインポートするための3つの主要な戦略を以下にベンチマークします。

    1. 単一のinsert ステートメント 、質問のように:

      INSERT INTO test (col1,col2,col3) VALUES (1,2,3)

    2. 複数のinsert ステートメント 、次のようにフォーマットされています:

      INSERT INTO test (col1,col2,col3) VALUES (1,2,3),(4,5,6),(7,8,9)

    3. load data infile ステートメント 、つまり、以前に作成したCSVファイルをmysqlにロードします :

      LOAD DATA INFILE 'the_dump.csv' INTO TABLE test

    RMySQLを使用しています ここでは、他のmysqlドライバーでも同様の結果が得られるはずです。 SQLテーブルは次のようにインスタンス化されました:

    CREATE TABLE `test` (
      `col1` double, `col2` double, `col3` double, `col4` double, `col5` double
    ) ENGINE=MyISAM;
    

    接続とテストデータはRで作成されました と:

    library(RMySQL)
    con = dbConnect(MySQL(),
                    user = 'the_user',
                    password = 'the_password',
                    host = '127.0.0.1',
                    dbname='test')
    
    n_rows = 1000000 # number of tuples
    n_cols = 5 # number of fields
    dump = matrix(runif(n_rows*n_cols), ncol=n_cols, nrow=n_rows)
    colnames(dump) = paste0('col',1:n_cols)
    

    単一のinsertのベンチマーク ステートメント:

    before = Sys.time()
    for (i in 1:nrow(dump)) {
      query = paste0('INSERT INTO test (',paste0(colnames(dump),collapse = ','),') VALUES (',paste0(dump[i,],collapse = ','),');')
      dbExecute(con, query)
    }
    time_naive = Sys.time() - before 
    

    =>これには約4分かかります 私のコンピューターで

    複数のinsertのベンチマーク ステートメント:

    before = Sys.time()
    chunksize = 10000 # arbitrary chunk size
    for (i in 1:ceiling(nrow(dump)/chunksize)) {
      query = paste0('INSERT INTO test (',paste0(colnames(dump),collapse = ','),') VALUES ')
      vals = NULL
      for (j in 1:chunksize) {
        k = (i-1)*chunksize+j
        if (k <= nrow(dump)) {
          vals[j] = paste0('(', paste0(dump[k,],collapse = ','), ')')
        }
      }
      query = paste0(query, paste0(vals,collapse=','))
      dbExecute(con, query)
    }
    time_chunked = Sys.time() - before 
    

    =>これには約40秒かかります 私のコンピューターで

    ベンチマークload data infile ステートメント

    before = Sys.time()
    write.table(dump, 'the_dump.csv',
              row.names = F, col.names=F, sep='\t')
    query = "LOAD DATA INFILE 'the_dump.csv' INTO TABLE test"
    dbSendStatement(con, query)
    time_infile = Sys.time() - before 
    

    =>これには約4秒かかります 私のコンピューターで

    多くの挿入値を処理するSQLクエリを作成することは、パフォーマンスを向上させる最も簡単な方法です。 LOAD DATA INFILEへの移行 最適な結果につながります。優れたパフォーマンスのヒントは、mysqlドキュメントのこのページにあります




    1. クエリが終了する前にクエリ結果の表示を開始する

    2. クエリパフォーマンスインサイト:Azure SQLデータベースのリソースを消費するものを発見しますか?

    3. laravelデータベース接続が未定義のインデックスエラーを返す

    4. mysqlの文字列の長さに制限はありますか?