pyodbc:使用fast_executemany和TEXT / NTEXT列时出现内存错误

时间:2019-05-02 20:46:55

标签: sql-server python-3.x pyodbc

在将行插入数据库时​​遇到问题。只是想知道是否有人对此有何想法?当我避免使用fast_executemany但插入变得非常慢时,它会起作用。

driver = 'ODBC Driver 17 for SQL Server'
conn = pyodbc.connect('DRIVER=' + driver + ';SERVER=' + server+ \
                      ';UID=' + user+ ';PWD=' + password)
cursor = conn.cursor()
cursor.fast_executemany = True

insert_sql = """
INSERT INTO table (a, b, c)
VALUES (?, ?, ?)
"""

cursor.executemany(insert_sql, insert_params)

---------------------------------------------------------------------------
MemoryError                               Traceback (most recent call last)
<ipython-input-12-e7e82e4d8c2d> in <module>
      2 start_time = time.time()
      3 
----> 4 cursor.executemany(insert_sql, insert_params)

MemoryError:

2 个答案:

答案 0 :(得分:1)

使用fast_executemanyTEXT列时,NTEXT存在一个已知问题,如GitHub here所述。

问题在于,当pyodbc查询数据库元数据以确定列的最大大小时,驱动程序将返回2 GB(而不是0,而将返回[n]varchar(max)列)。

pyodbc为参数数组中的每个[N]TEXT元素分配2 GB的内存,Python应用程序很快用完了内存。

解决方法是使用cursor.setinputsizes([(pyodbc.SQL_WVARCHAR, 0, 0)])(如here所述)诱骗pyodbc将[N]TEXT列视为[n]varchar(max)列。

(鉴于[N]TEXT是SQL Server弃用的列类型,因此不太可能对此问题进行正式修复。)

答案 1 :(得分:0)

虽然 Gord Thompson's answer 为 OP 解决了这个问题,但我想指出,所写的问题适用于可能发生 MemoryError 的其他情况,而 fast_executemany 实际上可以抛出在其他情况下,不仅仅是使用 [N]TEXT 列。

就我而言,在尝试一次 MemoryError 数百万条记录时抛出了 INSERT,并且作为 noted here,“参数值保存在内存中,因此非常大量的记录(数千万或更多)可能会导致内存问题”。不一定需要几千万才能触发,所以YMMV。

对此的一个简单解决方案是确定每次执行时要批处理的合理数量的记录。以下是使用 Pandas 数据框作为源的示例(照常建立您的 insert_query):

batch_size = 5000  # Set to a desirable batch size
with connection.cursor() as cursor:
    try:
        cursor.fast_executemany = True

        # Iterate each batch chunk using numpy's split
        for chunk in np.array_split(df, batch_size):
            cursor.executemany(insert_query,
                               chunk.values.tolist())

        # Run a single commit at the end of the transaction
        connection.commit()

    except Exception as e:
        # Rollback on any exception
        connection.rollback()
        raise e

希望这可以帮助遇到此问题并且没有在他们的目标上有任何 [N]TEXT 列的任何人!