将数据帧从R插入SQL的有效方法

use*_*523 4 mysql r rodbc

我有一个包含1000万行和5列的数据框,我想插入到现有的sql表中.请注意,我没有创建表的权限,我只能将值插入现有表中.我目前正在使用RODBCext

query_ch <- "insert into [blah].[dbo].[blahblah] 
               (col1, col2, col3, col4, col5)
               values (?,?,?,?,?)"

sqlExecute(channel, query_ch, my_data) 
Run Code Online (Sandbox Code Playgroud)

这需要太长时间(超过10小时).有没有办法更快地完成这个?

Jea*_*lie 8

TL; DR: LOAD DATA INFILE比多个INSERT语句快一个数量级,多个语句本身比单个INSERT语句快一个数量级.

我将以下三个主要策略的基准测试从R导入到Mysql:

  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;
Run Code Online (Sandbox Code Playgroud)

连接和测试数据创建于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)
Run Code Online (Sandbox Code Playgroud)

对单个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 
Run Code Online (Sandbox Code Playgroud)

=>这在我的电脑上大约需要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 
Run Code Online (Sandbox Code Playgroud)

=>这在我的电脑上大约需要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 
Run Code Online (Sandbox Code Playgroud)

=>这在我的电脑上大约需要4秒钟


制作SQL查询以处理许多插入值是提高性能的最简单方法.过渡到LOAD DATA INFILE将导致最佳结果.在这个mysql文档页面中可以找到好的性能提示.