【问题标题】:Pandas dataframe insert into SQL Server taking too long with execute and executemany将 Pandas 数据框插入 SQL Server 执行和执行的时间过长
【发布时间】:2021-03-30 04:07:09
【问题描述】:

我有一个包含 27 列和约 45k 行的 pandas 数据框,我需要将其插入到 SQL Server 表中。

我目前正在使用以下代码,插入需要 90 分钟:

conn = pyodbc.connect('Driver={ODBC Driver 17 for SQL Server};\
                   Server=@servername;\
                   Database=dbtest;\
                   Trusted_Connection=yes;')
cursor = conn.cursor()  #Create cursor



 for index, row in t6.iterrows():

    cursor.execute("insert into dbtest.dbo.test( col1, col2, col3, col4,col5,col6,col7,col8,col9,col10,col11,col12,col13,col14,,col27)\
                                                        values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
                                                        row['col1'],row['col2'], row['col3'],,row['col27'])

我也尝试过使用 executemany 加载,这需要更长的时间才能完成,将近 120 分钟。

我真的在寻找更快的加载时间,因为我需要每天运行它。

【问题讨论】:

标签: sql-server pandas pyodbc execute


【解决方案1】:

我过去已经解决了这个问题,这是我使用 sqlalchemy 可以让它工作的最快速度。

import sqlalchemy as sa
engine = (sa.create_engine(f'mssql://@{server}/{database}
          ?trusted_connection=yes&driver={driver_name}', fast_executemany=True)) #windows authentication
df.to_sql('Daily_Report', con=engine, if_exists='append', index=False)

如果引擎不适合您,那么您可能有不同的设置,请参阅:https://docs.sqlalchemy.org/en/13/core/engines.html

您应该能够创建上面需要的变量,但这是我获得driver 的方式:

driver_name = ''
driver_names = [x for x in pyodbc.drivers() if x.endswith(' for SQL Server')]
if driver_names:
    driver_name = driver_names[-1] #You may need to change the [-1] if wrong driver to [-2] or a different option in the driver_names list.
if driver_name:
    conn_str = f'''DRIVER={driver_name};SERVER='''
else:
    print('(No suitable driver found. Cannot connect.)')

【讨论】:

  • 上面抛出了一个错误'TypeError: to_sql() got an unexpected keyword argument 'uri''。我尝试在单独的变量中创建引擎,但这也不起作用..
  • @user10 对不起,我假设daskpandas 具有相同的to_sql 函数参数。我会用pandas 的方式更新我的答案。
  • 所以,我有一份每日报告,我在 2000 万行中使用 dask,这需要大约一个小时,使用所有相同的方法,但将 con 传递给 pandas 而不是uridask。我认为to_sql 应该在daskpandas 上具有相似的性能,所以上面的代码现在可以为您工作,并且在一个小数据帧的情况下它很快。
【解决方案2】:

您可以在 pyodbc 本身中为版本>=4.0.19 设置 fast_executemany。默认关闭。

import pyodbc

server_name = 'localhost'
database_name = 'AdventureWorks2019'
table_name = 'MyTable'
driver = 'ODBC Driver 17 for SQL Server'

connection = pyodbc.connect(driver='{'+driver+'}', server=server_name, database=database_name, trusted_connection='yes') 

cursor = connection.cursor()

cursor.fast_executemany = True   # reduce number of calls to server on inserts

# form SQL statement
columns = ", ".join(df.columns)

values = '('+', '.join(['?']*len(df.columns))+')'
      
statement = "INSERT INTO "+table_name+" ("+columns+") VALUES "+values

# extract values from DataFrame into list of tuples
insert = [tuple(x) for x in df.values]

cursor.executemany(statement, insert)

或者,如果您更喜欢直接使用 sqlalchemy 和数据框。

import sqlalchemy as db

engine = db.create_engine('mssql+pyodbc://@'+server_name+'/'+database_name+'?trusted_connection=yes&driver='+driver, fast_executemany=True)

df.to_sql(table_name, engine, if_exists='append', index=False)

在此链接中查看 fast_executemany。

https://github.com/mkleehammer/pyodbc/wiki/Features-beyond-the-DB-API

【讨论】:

    【解决方案3】:

    您可以尝试使用 pandas to_sql 内置的方法'multi'。

    df.to_sql('table_name', con=engine, if_exists='replace', index=False, method='multi')
    

    multi 方法允许您“在单个 INSERT 子句中传递多个值”。每个文档。 我发现它非常有效。

    【讨论】:

      猜你喜欢
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-10
      • 2018-01-19
      • 2023-03-16
      相关资源
      最近更新 更多