cx_Oracle:fetchall() 停止使用大 SELECT 语句

cx_Oracle: fetchall() stops working with big SELECT statements

我正在尝试从 oracle 数据库中读取数据。 我必须阅读 python 一个简单 select 的结果 returns 一百万行。

我使用fetchall()函数,改变游标的数组大小属性。

select_qry = db_functions.read_sql_file('src/data/scripts/03_perimetro_select.sql')
dsn_tns = cx_Oracle.makedsn(ip, port, sid)
con = cx_Oracle.connect(user, pwd, dsn_tns)


start = time.time()

cur = con.cursor()
cur.arraysize = 1000
cur.execute('select * from bigtable where rownum < 10000')
res = cur.fetchall()
# print res  # uncomment to display the query results
elapsed = (time.time() - start)
print(elapsed, " seconds")
cur.close()
con.close()

如果我删除 where 条件 where rownum < 10000,python 环境会冻结并且 fetchall() 函数永远不会结束。

经过一些试验,我发现这个精确的限制 select,它可以工作到 50k 行,但是如果我 select 60k 行它就会失败。

是什么导致了这个问题?我是否必须找到另一种方法来获取这么多数据,或者问题出在 ODBC 连接上?我该如何测试?

考虑使用 Oracle 的 ROWNUM 批量 运行。要合并回单个对象,请附加到不断增长的列表中。下面假设 table 的总行数是 1 mill。根据需要调整:

table_row_count = 1000000
batch_size = 10000

# PREPARED STATEMENT
sql = """SELECT t.* FROM
            (SELECT *, ROWNUM AS row_num 
             FROM 
                (SELECT * FROM bigtable ORDER BY primary_id) sub_t
            ) AS t
         WHERE t.row_num BETWEEN :LOWER_BOUND AND :UPPER_BOUND;"""

data = []
for lower_bound in range(0, table_row_count, batch_size):
    # BIND PARAMS WITH BOUND LIMITS
    cursor.execute(sql, {'LOWER_BOUND': lower_bound, 
                         'UPPER_BOUND': lower_bound + batch_size - 1})

    for row in cur.fetchall():
       data.append(row)

您可能 运行 计算机内存不足 运行 cx_Oracle。不要使用 fetchall() 因为这将需要 cx_Oracle 将所有结果保存在内存中。使用类似这样的方法来获取批量记录:

cursor = connection.cursor()
cursor.execute("select employee_id from employees")
res = cursor.fetchmany(numRows=3)
print(res)
res = cursor.fetchmany(numRows=3)
print(res)

fetchmany() 调用置于循环中,在获取下一组行之前处理应用中的每批行,并在没有更多数据时退出循环。

无论您使用什么解决方案,调整 cursor.arraysize 以获得最佳性能。

已经给出的重复查询和 select 行子集的建议也值得考虑。如果您使用的是 Oracle DB 12,则有一种更新(更简单)的语法,例如 SELECT * FROM mytab ORDER BY id OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY.

PS cx_Oracle 不使用 ODBC。