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 Server2008DBに挿入する際にも同様の問題が発生していました。SQL側でプロファイラートレースを実行したとき、pyODBCは接続を作成し、パラメーター化された挿入ステートメントを準備して、それを1行に対して実行していました。次に、ステートメントの準備を解除し、接続を閉じます。次に、行ごとにこのプロセスを繰り返しました。

これを行わなかったソリューションをpyODBCで見つけることができませんでした。SQL Serverに接続するためにceODBCに切り替えることになり、パラメーター化されたステートメントが正しく使用されました。

于 2012-03-29T19:43:51.783 に答える
5

pyodbcを使用して+2M行をMSSQLに挿入しようとすると、Postgres(psycopg2)およびOracle(cx_Oracle)での一括操作と比較して、非常に長い時間がかかりました。BULK INSERT操作を使用する権限がありませんでしたが、以下の方法で問題を解決することができました。

多くのソリューションはfast_executemanyを正しく提案しましたが、正しく使用するにはいくつかのトリックがあります。まず、connectメソッドでautocommitがTrueに設定されている場合、各行の後にpyodbcがコミットしていることに気付きました。したがって、これはFalseに設定する必要があります。また、一度に約20k行を挿入すると、非線形の速度低下が見られました。つまり、10k行の挿入は1秒未満でしたが、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

pypyODBC w /python3.5とMicrosoftSQLServerManagementStudioを使用していました。特定のテーブル(40変数で約70K行)は、pypyodbcで.executemany()メソッドを使用してINSERTするのに112秒かかりました。

ceODBCでは4秒かかりました。

于 2017-02-26T21:51:36.133 に答える