我试图使用cx_Oracle从ORACLE 12c数据库中选择数据,但我得到了异常:“cx_Oracle.OperationalError:ORA-03113:通信通道上的文件结束”。
我的查询使用Pycharm(jdbc:oracle:瘦驱动程序)表现良好。但是,在python 3.6中使用cx_Oracle,除非我将IN子句中的ID数从500减少到250,否则查询将失败.Corrsor.fetchall()函数抛出异常。我没有权限访问数据库以检查锁或加载等问题,但这些问题可能是导致问题的原因吗?根据我们的DBA,Oracle数据库服务器没有任何问题,因为查询工作正常,我倾向于相信它。我也搞乱了客户端sqlnet.ora,它允许最终抛出异常而不是永远挂起,但我仍然无法获取数据。
def select(self, query, *args):
cur = self.dbh.cursor()
cur.prepare(query)
try:
cur.execute(None, args)
return cur.fetchall()
# my attempt to handle the issue
except (cx_Oracle.OperationalError, cx_Oracle.DatabaseError) as e:
# cx_Oracle.OperationalError: ORA-03113: end-of-file on communication channel
self.logger.error('Oracle Error: {}'.format(traceback.format_exc()))
raise e
代码调用选择这样。为简洁起见,我省略了完整的字符串ID
ids = ['1', '2', '3', ...]
query = """\
select * from my_table where id in(:0,:1,:2,:3,:4, ...)
"""
self.select(query, *ids)
查询在没有占位符的情况下失败(ID也直接放在查询中)。
我希望能够使用最多1000个ID的IN子句运行任何选择查询,而不会收到ORA-03113异常。
编辑:我在ubuntu 18.04.2上安装了oracle-instantclient18.5-basic-18.5.0.0.0-3.x86_64.rpm *,有cx_Oracle版本7.1.2,我正在连接到Oracle 12.1.0.2.0。
该查询位于BMC Software的ARS的基础表中。我将开始尝试使用本地表结构复制问题,但这是一个烂摊子,需要一些时间。如果我能够创建表的本地副本,我不确定我是否能够复制该问题,因为具有不同ID的相同查询可以正常工作。这使得它似乎是数据驱动的,但是,在我将查询减少到250个ID之后,我将250从上半部分交换到下半部分,并获得了相同的成功结果,因此它似乎不仅仅是一个坏行。
我可以在客户端启用更多有用的日志记录以获取更多信息吗?
Edit2:我还应该补充说,问题不只发生在一个查询中。我已经看到了对完全不同的表选择查询的相同问题。
编辑3:我刚刚发现通过注释掉我正在选择的一些列也可以使查询起作用。像这样的列:
to_char(to_date('1970-01-01','YYYY-MM-DD') + numtodsinterval(EventStart,'SECOND'),'YYYY-MM-DD HH24:MI:SS')
这可能表示正在达到某种超时,可能在我的sqlnet.ora中配置,也可能没有配置:
DISABLE_OOB=on
SQLNET.RECV_TIMEOUT=60
SQLNET.SEND_TIMEOUT=60
TCP.CONNECT_TIMEOUT=300
SQLNET.OUTBOUND_CONNECT_TIMEOUT=300
ENABLE=BROKEN
TRACE_LEVEL_CLIENT=ADMIN
TRACE_FILE_CLIENT=sqlnet
编辑4:我尝试了更多的东西。
我安装了相同版本的即时客户端,除了在Windows 7机器上,并对同一个数据库实例运行相同的查询。查询成功。
我还缩小了对于这个特定查询,它将接受499个ID,但是失败了500.我从查询中注释掉哪个ID无关紧要。
我还尝试通过使用子选择来欺骗查询以认为ID较少:
IN(
select regexp_substr(:0,'[^,]+', 1, level) from dual connect by regexp_substr(:0, '[^,]+', 1, level) is not null
)
我收到错误“cx_Oracle.DatabaseError:ORA-01460:未实现或无理转换请求”,之后我意识到这是有道理的,因为Oracle只允许一个字符串长达4000字节。
我想我终于找到了让一切运转起来的方法。我终于遇到了这个链接:
https://ardentperf.com/2010/09/08/mysterious-oracle-net-errors/
事实证明,这解决了我的问题。我仍然无法让cx_Oracle遵守与tnsnames.ora文件格式相同的连接字符串,但我现在更改了我的代码以引用tnsnames.ora,如下所示:
connection_info = {
'user': self.config.get(self.db, 'user'),
'pass': self.config.get(self.db, 'password')
}
connection_string = '{user}/{pass}@TEST'\
.format(**connection_info)
connection = cx_Oracle.connect(connection_string)
我的tnsnames.ora包含以下内容:
TEST =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(Host = myhost.com)(Port = 1521))
)
(SDU=1024)
(CONNECT_DATA =
(SID=mysid)
)
)
这里的关键是SDU = 1024,它无法解决这个问题。
https://docs.oracle.com/cd/B28359_01/network.111/b28317/sqlnet.htm#NETRF184
上面链接中的文档表明SDU的默认值是8192字节(8 KB),我的理解是应该自动协商该值。这似乎不是这种情况,我不知道过去的默认值是什么。