【问题标题】:pyodbc: Memory Error using fast_executemany with TEXT / NTEXT columnspyodbc:使用带有 TEXT / NTEXT 列的 fast_executemany 的内存错误
【发布时间】:2019-09-21 10:08:09
【问题描述】:

我在将行插入数据库时​​遇到问题。只是想知道是否有人知道为什么会这样?当我避免使用 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:

【问题讨论】:

  • 碰巧你是在 Linux 机器上运行这个 Python 代码吗?如果是这样,它是什么分布/环境?
  • 你用的是什么版本的pyodbc?
  • @GordThompson 我在 Windows 10 上运行它
  • @EdekiOkoh 它正在使用 4.0.25
  • 该表是否有任何TEXTNTEXT 列?如果是这样,那么this GitHub issue 可能会很有趣。

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


【解决方案1】:

在使用 TEXTNTEXT 列时,fast_executemany 存在一个已知问题,如 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 已弃用的列类型,因此不太可能正式修复此问题。)

【讨论】:

    【解决方案2】:

    虽然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 列的任何人!

    【讨论】:

      【解决方案3】:

      就我而言,MemoryError 是因为我使用了一个非常旧的驱动程序“SQL Server”。切换到较新的驱动程序('ODBC Driver 17 for SQL Server'),如下面的链接中所述,它可以工作: link

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2018-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-27
      • 2023-03-30
      • 2019-01-10
      • 1970-01-01
      • 2019-08-18
      相关资源
      最近更新 更多