如何加快将数据帧导出到MS SQL Server的速度? R快一倍

时间:2019-04-05 09:40:55

标签: pandas sqlalchemy bulkinsert pyodbc pymssql

我非常清楚有很多类似的问题,并且大多数答案都暗示了诸如CSV批量插入(这比从R导出更让人头疼)之类的事情。

但是,我不知道与R进行任何比较,它可以解决pandas.DataFrame.to_sql()是否能够达到与R的DBI::dbWriteTable相同的速度。

我在Windows 10机器上运行Anaconda。作为测试,我将导出15个表,每个表具有10,000行,30列随机浮点数和3个字符串列。

时间是:

  • 没有fast_executemany的pyodbc:19秒
  • 具有fast_executemany的pyodbc:7秒
  • turbodbc:13秒
  • R和DBI :: dbWriteTable:4秒

我尝试了pymssql,但无法正常工作(请参见代码中的注释)。

fast_executemany是一个很大的改进,但仍需要花费R几乎两倍的时间。与R的区别令人惊讶!并且这也表明该连接本质上没有错到SQL服务器。

如果答案是:“不,R会更快,周期”,那么有人可以解释为什么吗?了解造成差异的原因将非常有趣。

对于巨型表,我可能会考虑将其导出为镶木地板或羽毛状的格式,让R读取它们,然后导出为R的SQL。但是,对于中型表(30到200 MB数据之间的任何数据)我宁愿在Python中找到更好的解决方案。

PS这是一个SQL Server Express,它运行在与我运行Python和R相同的PC上。

编辑

为回应评论,我比较了不同大小的执行时间,R的时间与pyodbc的时间比例似乎相当恒定。一点点地考虑一下-我不是数据库专家,很可能我缺少数据库配置或其他因素,这些因素正在起作用。底线仍然是,就我而言,R几乎快两倍。

enter image description here

我的代码:

import numpy as np
import pandas as pd
import timeit
from sqlalchemy import create_engine #, MetaData, Table, select
import pymssql
ServerName = myserver
Database = mydb
params = '?driver=SQL+Server+Native+Client+11.0'

# we define the pyodbc connection
engine_pyo = create_engine('mssql+pyodbc://' + ServerName + '/' + Database + params,
                           encoding='latin1', fast_executemany=True)
conn_pyo = engine_pyo.connect()

# now the turbodbc
engine_turbo = create_engine('mssql+turbodbc://' + ServerName + '/' + Database + params, encoding='latin1')
conn_turbo = engine_turbo.connect()

# pymssql is installed but doesn't work.
# I get:
# connect() got an unexpected keyword argument 'driver'
#engine_pyms = create_engine('mssql+pymssql://' + ServerName + '/' + Database + params, encoding='latin1')
#conn_pyms = engine_pyms.connect()

sheets = 15
rows= int(10e3)

def create_data(sheets, rows):
    df = {} # dictionary of dataframes
    for i in range(sheets):
        df[i] = pd.DataFrame(data= np.random.rand(rows,30) )
        df[i]['a'] = 'some long random text'
        df[i]['b'] = 'some more random text'
        df[i]['c'] = 'yet more text'
    return df

def data_to_sql(df, conn):
    for d in df:
        df[d].to_sql('Test ' + str(d) , conn, if_exists='replace' )

#NB: df is a dictionary containing dataframes - it is NOT a dataframe
# df[key] is a dataframe
df = create_data(sheets, rows)

rep = 1
n = 1
t_pyodbc = timeit.Timer( "data_to_sql(df, conn_pyo)" , globals=globals() ).repeat(repeat = rep, number = n)
t_turbo = timeit.Timer( "data_to_sql(df, conn_turbo)" , globals=globals() ).repeat(repeat = rep, number = n)

我的R代码:

library(tictoc)
library(readxl)
library(tidyverse)
library(dplyr)
library(dbplyr)
library(DBI)

tic("data creation")
rows = 10000
cols = 30
tables = 15

dataframes <- list()

for (i in 1:tables){

newdf <- as_tibble( matrix(rnorm(rows * cols),rows , cols) ) 
newdf <- mutate(newdf, a = 'some long random text') 
newdf <- mutate(newdf, b = 'some more random text') 
newdf <- mutate(newdf, c = 'yet more text')

dataframes[[i]] <- newdf

}
toc()

tic("set up odbc")
con <- DBI::dbConnect(odbc::odbc(),
                      driver = "SQL Server",
                      server = "LTS-LNWS010\\SQLEXPRESS",
                      database = "CDL",
                      trusted_connection = TRUE)
toc()

tic("SQL export")
for(i in seq_along(dataframes)){
  DBI::dbWriteTable(con, paste("Test_", i), dataframes[[i]], overwrite = TRUE)
}
toc()

0 个答案:

没有答案