Jul*_*ore 5 python performance sqlalchemy pyodbc
在回答我的问题How to speed up data wrangling A LOT in Python + Pandas + sqlAlchemy + MSSQL/T-SQL 时,@IljaEverilä亲切地指导我使用 pyODBC 的 fast_executemany加速 pandas.DataFrame.to_sql。
NB 出于测试目的,我只读取/写入 10k 行。
我添加了事件侦听器,并且 a) 调用了该函数,但是 b) 显然 executemany 未设置,因为 IF 失败且 cursor.fast_executemay 未设置。
def namedDbSqlAEngineCreate(dbName):
# Create an engine and switch to the named db
# returns the engine if successful and None if not
# 2018-08-23 added fast_executemany accoding to this /sf/ask/3360458601/?rq=1
engineStr = 'mssql+pyodbc://@' + defaultDSN
engine = sqla.create_engine(engineStr, echo=False)
@event.listens_for(engine, 'before_cursor_execute')
def receive_before_cursor_execute(conn, cursor, statement, params, context, executemany):
# print("FUNC call")
if executemany:
print('executemany')
cursor.fast_executemany = True
try:
engine.execute('USE ' +dbName)
return(engine)
except sqla.exc.SQLAlchemyError as ex:
if ex.orig.args[0] == '08004':
print('namedDbSqlAEngineCreate:Database %s does not exist' % dbName)
else:
print(ex.args[0])
return(None)
Run Code Online (Sandbox Code Playgroud)
速度自然没有变化。
我原来问题中的代码在 to_sql 中没有变化
nasToFillDF.to_sql(name=tempTableName, con=engine.engine, if_exists='replace', chunksize=100, index=False)
Run Code Online (Sandbox Code Playgroud)
因为我尝试过,根据示例,设置 chunksize = None 并收到错误消息(我之前遇到过)
(pyodbc.ProgrammingError) ('SQL 包含 -31072 个参数标记,但提供了 100000 个参数', 'HY000')
我做错了什么?我猜没有设置 receive_before_cursor_execute 的 executemany 参数,但如果这是答案,我不知道如何解决它。
安装程序是 pyodbc 4.0.23、sqlAchemy 1.2.6、Python 3.6.something
您收到的错误是由熊猫版本0.23.0中引入的更改,在0.23.1恢复引起的,并在0.24.0重新作为解释在这里。生成的 VALUES 子句包含 100,000 个参数标记,并且计数似乎存储在一个有符号的 16 位整数中,所以它溢出了,你会觉得很有趣
SQL 包含 -31072 个参数标记,但提供了 100000 个参数
您可以自行检查:
In [16]: 100000 % (2 ** 16) - 2 ** 16
Out[16]: -31072
Run Code Online (Sandbox Code Playgroud)
如果您想继续按原样使用 Pandas,您必须计算并提供一个合适的chunksize
值,例如您使用的 100,同时考虑到 VALUES 子句的最大行限制 1,000 和最大参数限制2,100 用于存储过程。详细信息在链接的 Q/A中再次解释。
在更改之前 Pandas 过去总是executemany()
在插入数据时使用。较新的版本检测所使用的方言是否支持 INSERT 中的 VALUES 子句。这种检测发生在SQLTable.insert_statement()
并且无法控制,这是一种耻辱,因为PyODBC修复了它们的executemany()
性能,因为正确的标志是 enabled。
为了强制 Pandasexecutemany()
再次与 PyODBC一起使用,SQLTable
必须进行monkeypatched:
import pandas.io.sql
def insert_statement(self, data, conn):
return self.table.insert(), data
pandas.io.sql.SQLTable.insert_statement = insert_statement
Run Code Online (Sandbox Code Playgroud)
如果未设置标志,这将非常缓慢,Cursor.fast_executemany
因此请记住设置正确的事件处理程序。
这是一个简单的性能比较,使用以下数据帧:
In [12]: df = pd.DataFrame({f'X{i}': range(1000000) for i in range(9)})
Run Code Online (Sandbox Code Playgroud)
香草熊猫 0.24.0:
In [14]: %time df.to_sql('foo', engine, chunksize=209)
CPU times: user 2min 9s, sys: 2.16 s, total: 2min 11s
Wall time: 2min 26s
Run Code Online (Sandbox Code Playgroud)
启用快速执行功能的 Monkeypatched Pandas:
In [10]: %time df.to_sql('foo', engine, chunksize=500000)
CPU times: user 12.2 s, sys: 981 ms, total: 13.2 s
Wall time: 38 s
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
6737 次 |
最近记录: |