Dataframe на SQL Сервер с помощью Execute many from pyodbc - PullRequest
0 голосов
/ 07 апреля 2020

Я пытаюсь загрузить данные из фрейма данных на SQL Сервер, использующий Pyodb c, который вставляет строку за строкой, и он очень медленный. улучшения производительности не наблюдается.

Попытка запустить SQL azure, поэтому SQL Алхимия - не простой способ подключения. пожалуйста, найдите подходы, которым я следовал, и есть ли другой способ улучшить производительность массовой загрузки.

Метод 1

 cursor = sql_con.cursor()
cursor.fast_executemany = True
for row_count in range(0, df.shape[0]):
  chunk = df.iloc[row_count:row_count + 1,:].values.tolist()
  tuple_of_tuples = tuple(tuple(x) for x in chunk)
  for index,row in ProductInventory.iterrows():
  cursor.executemany("INSERT INTO table ([x]],[Y]) values (?,?)",tuple_of_tuples)

Метод 2

 cursor = sql_con.cursor() 
for row_count in range(0, ProductInventory.shape[0]):
      chunk = ProductInventory.iloc[row_count:row_count + 1,:].values.tolist()
      tuple_of_tuples = tuple(tuple(x) for x in chunk)
  for index,row in ProductInventory.iterrows():
    cursor.executemany(""INSERT INTO table ([x]],[Y]) values (?,?)",tuple_of_tuples 

Может кто-нибудь сказать мне, почему производительность не улучшается даже на 1%? Это все еще занимает столько же времени

Ответы [ 2 ]

1 голос
/ 07 апреля 2020

Попытка запустить в SQL azure, поэтому SQL Алхимия - не простой способ подключения.

Возможно, вам просто нужно сначала преодолеть это препятствие. Затем вы можете использовать pandas to_ sql вместе с fast_executemany=True. Например,

from sqlalchemy import create_engine
#
# ...
#
engine = create_engine(connection_uri, fast_executemany=True)
df.to_sql("table_name", engine, if_exists="append", index=False)

Если у вас есть работающая строка подключения pyodb c , вы можете преобразовать ее в SQLAlchemy URI подключения примерно так:

connection_uri = 'mssql+pyodbc:///?odbc_connect=' + urllib.parse.quote_plus(connection_string)
1 голос
/ 07 апреля 2020

Пара вещей

  1. Почему вы дважды перебираете ProductInventory?

  2. Разве не должен произойти вызов executemany после того, как вы собрали все tuple_of_tuples или их партию?

  3. В документации pyodb c сказано, что «запуск executemany () с fast_executemany = False обычно не будет намного быстрее, чем прямой запуск нескольких команд execute ()». Поэтому вам нужно установить cursor.fast_executemany=True в обоих примерах (см. https://github.com/mkleehammer/pyodbc/wiki/Cursor для более подробной информации / примеров). Я не уверен, почему это опущено в примере 2.

Вот пример того, как вы можете достичь sh того, что, я думаю, вы пытаетесь сделать. math.ceil и условное выражение в end_idx = ... учитывают последний пакет, который может быть нечетного размера. Итак, в приведенном ниже примере у вас есть 10 строк и размер пакета 3, так что вы получите 4 пакета, последний из которых имеет только один кортеж.

import math

df = ProductInventory
batch_size = 500
num_batches = math.ceil(len(df)/batch_size)

for i in range(num_batches):
    start_idx = i * batch_size
    end_idx = len(df) if i + 1 == num_batches else start_idx + batch_size
    tuple_of_tuples = tuple(tuple(x) for x in df.iloc[start_idx:end_idx, :].values.tolist())       
    cursor.executemany("INSERT INTO table ([x]],[Y]) values (?,?)", tuple_of_tuples)

Пример вывода:

=== Executing: ===
df = pd.DataFrame({'a': range(1,11), 'b': range(101,111)})

batch_size = 3
num_batches = math.ceil(len(df)/batch_size)

for i in range(num_batches):
    start_idx = i * batch_size
    end_idx = len(df) if i + 1 == num_batches else start_idx + batch_size
    tuple_of_tuples = tuple(tuple(x) for x in df.iloc[start_idx:end_idx, :].values.tolist())
    print(tuple_of_tuples)

=== Output: ===
((1, 101), (2, 102), (3, 103))
((4, 104), (5, 105), (6, 106))
((7, 107), (8, 108), (9, 109))
((10, 110),)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...