pyodbc/sqlAchemy 启用快速执行多个

pyodbc/sqlAchemy enable fast execute many

@IljaEverilä 回答我的问题 How to speed up data wrangling A LOT in Python + Pandas + sqlAlchemy + MSSQL/T-SQL I was kindly directed to

NB 出于测试目的,我只有 reading/writing 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 
    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)

速度自然没有变化

我原问题中的代码在to_sql

中没有改变
nasToFillDF.to_sql(name=tempTableName, con=engine.engine, if_exists='replace', chunksize=100, index=False)

因为我尝试按照示例设置 chunksize = None 并收到错误消息(我之前遇到过)

(pyodbc.ProgrammingError) ('The SQL contains -31072 parameter markers, but 100000 parameters were supplied', 'HY000')

我做错了什么?我想 receive_before_cursor_execute 的 executemany 参数没有设置,但如果这是答案,我不知道如何修复它。

安装程序是 pyodbc 4.0.23,sqlAchemy 1.2.6,Python 3.6.something

您收到的错误是由 Pandas 版本 0.23.0 中引入的更改引起的,在 0.23.1 中还原,并在 0.24.0 中重新引入,如 所述。生成的 VALUES 子句包含 100,000 个参数标记,而且计数似乎存储在一个带符号的 16 位整数中,所以它溢出了,你得到了有趣的

The SQL contains -31072 parameter markers, but 100000 parameters were supplied

你可以自己查一下:

In [16]: 100000 % (2 ** 16) - 2 ** 16
Out[16]: -31072

如果您想继续按原样使用 Pandas,您必须计算并提供合适的 chunksize 值,例如您使用的 100,同时考虑到VALUES 子句的最大行限制为 1,000,存储过程的最大参数限制为 2,100。详细信息在 .

中再次说明

更改 Pandas 之前总是使用 executemany() when inserting data. Newer versions detect if the dialect in use supports VALUES clause in INSERT. This detection happens in SQLTable.insert_statement() and cannot be controlled, which is a shame since PyODBC fixed their executemany() performance,

为了强制 Pandas 再次使用 PyODBC executemany() 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

如果未设置 Cursor.fast_executemany 标志,这将可怕地 缓慢,因此请记住设置正确的事件处理程序。

这是一个简单的性能比较,使用以下数据帧:

In [12]: df = pd.DataFrame({f'X{i}': range(1000000) for i in range(9)})

香草 Pandas 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

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