pyodbc - очень медленная скорость массовой вставки - PullRequest
21 голосов
/ 17 апреля 2011

С этой таблицей:

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:

  • подготовить оператор
  • связать все параметры
  • выполнить

Ответы [ 6 ]

10 голосов
/ 29 марта 2012

У меня была похожая проблема со вставкой pyODBC в базу данных SQL Server 2008 с помощью executemany (). Когда я запустил трассировку профилировщика на стороне SQL, pyODBC создавал соединение, подготавливал параметризованный оператор вставки и выполнял его для одной строки. Тогда это не подготовит заявление и закроет соединение. Затем он повторил этот процесс для каждой строки.

Я не смог найти ни одного решения в pyODBC, которое бы этого не делало. В итоге я переключился на ceODBC для подключения к SQL Server, и он правильно использовал параметризованные операторы.

3 голосов
/ 24 сентября 2018

Попытка вставить + 2M строк в MSSQL с использованием pyodbc заняла абсурдно много времени по сравнению с массовыми операциями в Postgres (psycopg2) и Oracle (cx_Oracle). У меня не было привилегий для использования операции BULK INSERT, но я смог решить проблему с помощью метода ниже.

Многие решения правильно предлагают fast_executemany, однако есть некоторые хитрости для его правильного использования. Во-первых, я заметил, что pyodbc выполнял коммит после каждой строки, когда для autocommit было установлено значение True в методе connect, поэтому для него должно быть установлено значение False. Я также наблюдал нелинейное замедление при вставке более чем ~ 20 тыс. Строк за раз, то есть вставка 10 тыс. Строк была подсекундной, но 50 тыс. Выше 20 с. Я предполагаю, что журнал транзакций становится довольно большим и замедляет все это. Поэтому вы должны разделить свою вставку и зафиксировать после каждого фрагмента. Я обнаружил, что 5 тыс. Строк на чанк обеспечивают хорошую производительность, но это, очевидно, будет зависеть от многих факторов (данных, машины, конфигурации БД и т. Д.).

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()
3 голосов
/ 06 апреля 2012

Пробовал оба ceODBC и mxODBC, и оба также были мучительно медленными.Закончилось соединение с adodb с помощью http://www.ecp.cc/pyado.html. Общее время выполнения увеличилось в 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()
2 голосов
/ 01 ноября 2017

pyodbc 4.0.19 добавил опцию Cursor#fast_executemany, чтобы помочь решить эту проблему. Подробнее см. этот ответ .

1 голос
/ 31 мая 2017

Я записал данные в текстовый файл, а затем вызвал утилиту BCP.Гораздо быстрее.От 20 до 30 минут до нескольких секунд.

0 голосов
/ 27 февраля 2017

Я использовал pypyODBC с python 3.5 и Microsoft SQL Server Management Studio.Для конкретной таблицы (~ 70 тыс. Строк с 40 переменными) для вставки потребовалось 112 секунд, используя метод .executemany () с pypyodbc.

С ceODBC это заняло 4 секунды.

...