21

有了这张表:

CREATE TABLE test_insert (
    col1 INT,
    col2 VARCHAR(10),
    col3 DATE
)

以下代码需要 40 秒才能运行:

import pyodbc

from datetime import date


conn = pyodbc.connect('DRIVER={SQL Server Native Client 10.0};'
    'SERVER=localhost;DATABASE=test;UID=xxx;PWD=yyy')

rows = []
row = [1, 'abc', date.today()]
for i in range(10000):
    rows.append(row)

cursor = conn.cursor()
cursor.executemany('INSERT INTO test_insert VALUES (?, ?, ?)', rows)

conn.commit()

与 psycopg2 等效的代码只需要 3 秒。我不认为 mssql 比 postgresql 慢得多。关于在使用 pyodbc 时如何提高批量插入速度的任何想法?

编辑:在 ghoerz 的发现之后添加一些注释

在pyodbc中,流程executemany为:

  • 准备声明
  • 循环每组参数
    • 绑定参数集
    • 执行

在ceODBC中,流程executemany为:

  • 准备声明
  • 绑定所有参数
  • 执行
4

6 回答 6

11

我在使用 executemany() 将 pyODBC 插入 SQL Server 2008 DB 时遇到了类似的问题。当我在 SQL 端运行探查器跟踪时,pyODBC 正在创建一个连接,准备参数化的插入语句,并为一行执行它。然后它会取消准备语句,并关闭连接。然后它对每一行重复这个过程。

我无法在 pyODBC 中找到任何不这样做的解决方案。我最终切换到 ceODBC 来连接 SQL Server,它正确使用了参数化语句。

于 2012-03-29T19:43:51.783 回答
5

与 Postgres (psycopg2) 和 Oracle (cx_Oracle) 中的批量操作相比,尝试使用 pyodbc 将 +2M 行插入 MSSQL 花费的时间非常长。我没有使用 BULK INSERT 操作的权限,但能够通过以下方法解决问题。

许多解决方案正确地建议了 fast_executemany,但是,有一些技巧可以正确使用它。首先,我注意到当在 connect 方法中将 autocommit 设置为 True 时,pyodbc 在每一行之后提交,因此必须将其设置为 False。当一次插入超过 20k 行时,我还观察到非线性减速,即插入 10k 行是亚秒级,但 50k 超过 20 秒。我假设事务日志变得非常大并且减慢了整个事情的速度。因此,您必须在每个块之后分块您的插入和提交。我发现每块 5k 行提供了良好的性能,但这显然取决于许多因素(数据、机器、数据库配置等......)。

import pyodbc

CHUNK_SIZE = 5000

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in xrange(0, len(l), n): #use xrange in python2, range in python3
        yield l[i:i + n]

mssql_conn = pyodbc.connect(driver='{ODBC Driver 17 for SQL Server}',
                            server='<SERVER,PORT>',
                            timeout=1,
                            port=<PORT>,
                            uid=<UNAME>, 
                            pwd=<PWD>,
                            TDS_Version=7.2,
                            autocommit=False) #IMPORTANT

mssql_cur = mssql_conn.cursor()
mssql_cur.fast_executemany = True #IMPORTANT

params = [tuple(x) for x in df.values]

stmt = "truncate table <THE TABLE>"
mssql_cur.execute(stmt)
mssql_conn.commit()

stmt = """
INSERT INTO <THE TABLE> (field1...fieldn) VALUES (?,...,?)
"""
for chunk in chunks(params, CHUNK_SIZE): #IMPORTANT
    mssql_cur.executemany(stmt, chunk)
    mssql_conn.commit()
于 2018-09-24T08:05:12.563 回答
3

尝试了 ceODBC 和 mxODBC 并且两者都非常缓慢。最终在http://www.ecp.cc/pyado.html的帮助下建立了一个 adodb 连接。总运行时间提高了 6 倍!

comConn = win32com.client.Dispatch(r'ADODB.Connection')
DSN = 'PROVIDER=Microsoft.Jet.OLEDB.4.0;DATA SOURCE=%s%s' %(dbDIR,dbOut)
comConn.Open(DSN)

rs = win32com.client.Dispatch(r'ADODB.Recordset')
rs.Open('[' + tblName +']', comConn, 1, 3)

for f in values:
    rs.AddNew(fldLST, f)

rs.Update()
于 2012-04-05T21:03:47.023 回答
2

pyodbc 4.0.19 添加了一个Cursor#fast_executemany选项来帮助解决这个问题。有关详细信息,请参阅此答案

于 2017-11-01T15:31:21.220 回答
1

我将数据写入文本文件,然后调用 BCP 实用程序。快得多。从大约 20 到 30 分钟到几秒钟。

于 2017-05-31T19:45:46.713 回答
0

我使用的是带有 python 3.5 和 Microsoft SQL Server Management Studio 的 pypyODBC。使用带有 pypyodbc 的 .executemany() 方法插入特定表(约 70K 行 w/40 个变量)需要 112 秒。

使用 ceODBC 需要 4 秒。

于 2017-02-26T21:51:36.133 回答