任意选择在即时客户端上使用cx_Oracle失败

时间:2019-04-15 21:58:26

标签: python cx-oracle

我正在尝试使用cx_Oracle从ORACLE 12c数据库中选择数据,但出现异常:“ cx_Oracle.OperationalError:ORA-03113:通信通道上的文件结束”。

我的查询使用Pycharm(jdbc:oracle:thin驱动程序)运行良好。但是,在python 3.6中使用cx_Oracle时,查询失败,除非我将IN子句中的ID的数量从500减少到大约250。Cursor.fetchall()函数引发异常。我没有特权访问数据库以检查锁定或加载之类的东西,但是这些可能是问题的原因吗?根据我们的DBA,Oracle db服务器上没有任何错误,并且由于查询在其他方面可以正常运行,因此我倾向于相信它。我也搞砸了客户端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个替换为后半部分的ID,并获得了相同的成功结果,因此它似乎并不仅仅是一个糟糕的行。

是否可以在客户端启用更多有用的日志记录以获取更多信息?

Edit2:我还应该补充一点,这个问题不仅发生在一个查询中。我在选择查询到完全不同的表时遇到了相同的问题。

Edit3:我刚刚发现,通过注释掉我选择的某些列也可以使查询正常工作。像这样的列:

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机器上安装了相同版本的Instant Client,并针对相同的数据库实例运行了相同的查询。查询成功。

对于此特定查询,我也缩小了范围,它将接受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个字节。

1 个答案:

答案 0 :(得分:0)

我想我终于找到了使所有功能正常运行的方法。我终于遇到了这个链接:

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),我的理解是应该自动协商该值。情况似乎并非如此,我也不知道过去的默认设置是什么。