sql >> Databasteknik >  >> RDS >> Mysql

Effektivt sätt att infoga dataram från R till SQL

TL;DR: LOAD DATA INFILE är en storleksordning snabbare än flera INSERT satser, som i sig är en storleksordning snabbare än enstaka INSERT uttalanden.

Jag jämför nedan de tre huvudstrategierna för att importera data från R till Mysql:

  1. enkel insert uttalanden , som i frågan:

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

  2. flera insert uttalanden , formaterad så här:

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

  3. load data infile uttalande , dvs. ladda en tidigare skriven CSV-fil i mysql :

    LOAD DATA INFILE 'the_dump.csv' INTO TABLE test

Jag använder RMySQL här, men alla andra mysql-drivrutiner bör leda till liknande resultat. SQL-tabellen instansierades med:

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

Anslutningen och testdata skapades i R med:

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)

Benchmarking single insert uttalanden:

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 

=> detta tar ungefär 4 minuter på min dator

Benchmarking flera insert uttalanden:

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 

=> detta tar ungefär 40 sekunder på min dator

Benchmarking load data infile uttalande :

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 

=> detta tar ungefär 4 sekunder på min dator

Att skapa din SQL-fråga för att hantera många infogningsvärden är det enklaste sättet att förbättra prestandan. Övergår till LOAD DATA INFILE kommer att leda till optimala resultat. Bra prestandatips finns på denna sida med mysql-dokumentation .




  1. Handledning om SQL (DDL, DML) om exemplet på MS SQL Server-dialekt

  2. Lära 2 lägg till ett nytt fält som automatiskt genererar sekvensvärden

  3. PL/SQL - Exempel på höjning av applikationsfel

  4. Är SELECT-frågor den enda typen som kan kapslas?