Подтвердить что ты не робот

Как ускорить массовую вставку в MS SQL Server из CSV, используя pyodbc

Ниже мой код, с которым мне нужна помощь. Мне нужно запустить его более 1 300 000 строк, что означает, что для вставки ~ 300 000 строк требуется до 40 минут.

Я полагаю, что объемная вставка - это маршрут, чтобы ускорить его? Или это потому, что я повторяю строки через for data in reader: часть?

#Opens the prepped csv file
with open (os.path.join(newpath,outfile), 'r') as f:
    #hooks csv reader to file
    reader = csv.reader(f)
    #pulls out the columns (which match the SQL table)
    columns = next(reader)
    #trims any extra spaces
    columns = [x.strip(' ') for x in columns]
    #starts SQL statement
    query = 'bulk insert into SpikeData123({0}) values ({1})'
    #puts column names in SQL query 'query'
    query = query.format(','.join(columns), ','.join('?' * len(columns)))

    print 'Query is: %s' % query
    #starts curser from cnxn (which works)
    cursor = cnxn.cursor()
    #uploads everything by row
    for data in reader:
        cursor.execute(query, data)
        cursor.commit()

Я динамически выбираю заголовки столбцов специально (так как я хотел бы создать наиболее возможный пифонический код).

SpikeData123 - это имя таблицы.

4b9b3361

Ответ 1

BULK INSERT почти наверняка будет намного быстрее, чем чтение исходного файла по строкам и выполнение регулярного INSERT для каждой строки. Тем не менее, как BULK INSERT, так и BCP имеют значительное ограничение в отношении файлов CSV, поскольку они не могут обрабатывать текстовые классификаторы (ref: здесь). То есть, если ваш CSV файл не имеет в нем квалифицированных текстовых строк...

1,Gord Thompson,2015-04-15
2,Bob Loblaw,2015-04-07

... тогда вы можете BULK INSERT его, но если он содержит текстовые классификаторы (потому что некоторые текстовые значения содержат запятые)...

1,"Thompson, Gord",2015-04-15
2,"Loblaw, Bob",2015-04-07

... тогда BULK INSERT не сможет справиться с этим. Тем не менее, может быть, скорее всего, предварительно обработать такой CSV файл в файле с разделителями каналов...

1|Thompson, Gord|2015-04-15
2|Loblaw, Bob|2015-04-07

... или файл с разделителями табуляции (где представляет символ табуляции)...

1→Thompson, Gord→2015-04-15
2→Loblaw, Bob→2015-04-07

... и затем BULK INSERT этот файл. Для последнего (с разделителями табуляции) файла код BULK INSERT будет выглядеть примерно так:

import pypyodbc
conn_str = "DSN=myDb_SQLEXPRESS;"
cnxn = pypyodbc.connect(conn_str)
crsr = cnxn.cursor()
sql = """
BULK INSERT myDb.dbo.SpikeData123
FROM 'C:\\__tmp\\biTest.txt' WITH (
    FIELDTERMINATOR='\\t',
    ROWTERMINATOR='\\n'
    );
"""
crsr.execute(sql)
cnxn.commit()
crsr.close()
cnxn.close()

Примечание. Как указано в комментарии, выполнение инструкции BULK INSERT применимо только в том случае, если экземпляр SQL Server может напрямую читать исходный файл. Для случаев, когда исходный файл находится на удаленном клиенте, см. этот ответ.

Ответ 2

Как отмечено в комментарии к другому ответу, команда T-SQL BULK INSERT будет работать, только если файл, который будет импортирован, находится на том же компьютере, что и экземпляр SQL Server, или находится в сетевом расположении SMB/CIFS, что Экземпляр SQL Server может читать. Таким образом, это может быть неприменимо в случае, когда исходный файл находится на удаленном клиенте.

pyodbc 4.0.19 добавлена ​​функция Cursor # fast_executemany, которая может быть полезна в этом случае. fast_executemany по умолчанию отключен и следующий тестовый код...

cnxn = pyodbc.connect(conn_str, autocommit=True)
crsr = cnxn.cursor()
crsr.execute("TRUNCATE TABLE fast_executemany_test")

sql = "INSERT INTO fast_executemany_test (txtcol) VALUES (?)"
params = [(f'txt{i:06d}',) for i in range(1000)]
t0 = time.time()
crsr.executemany(sql, params)
print(f'{time.time() - t0:.1f} seconds')

... потребовалось около 22 секунд для выполнения на моей тестовой машине. Просто добавив crsr.fast_executemany = True...

cnxn = pyodbc.connect(conn_str, autocommit=True)
crsr = cnxn.cursor()
crsr.execute("TRUNCATE TABLE fast_executemany_test")

crsr.fast_executemany = True  # new in pyodbc 4.0.19

sql = "INSERT INTO fast_executemany_test (txtcol) VALUES (?)"
params = [(f'txt{i:06d}',) for i in range(1000)]
t0 = time.time()
crsr.executemany(sql, params)
print(f'{time.time() - t0:.1f} seconds')

... сократил время выполнения до чуть более 1 секунды.

Ответ 3

да встроенная вставка - это правильный путь для загрузки больших файлов в БД. С первого взгляда я бы сказал, что причина, по которой это так долго, - это то, как вы упомянули, что вы перебираете каждую строку данных из файла, что эффективно означает удаление преимуществ использования объемной вставки и превращение ее в обычную вставку. Просто помните, что, поскольку это название подразумевает, что оно используется для вставки патронов данных. Я бы удалил цикл и повторил попытку.

Также я бы дважды проверял ваш синтаксис для массовой вставки, поскольку он не выглядит корректным для меня. проверьте sql, который генерируется pyodbc, поскольку у меня возникает ощущение, что он может выполнять обычную вставку

Альтернативно, если он все еще медленный, я бы попытался использовать массивную вставку непосредственно из sql и либо загрузить весь файл в временную таблицу с объемной вставкой, а затем вставить соответствующий столбец в правые таблицы. или используйте сочетание объемной вставки и bcp для ввода определенных столбцов или OPENROWSET.

Ответ 4

Используйте библиотеку 'turbodbc', это был единственный способ быстрой загрузки.