温馨提示:本文翻译自stackoverflow.com,查看原文请点击:python - inserting CLOB to database selected from another
cx-oracle oracle python

python - 将CLOB插入到另一个数据库中

发布于 2020-05-20 15:12:38

我有两个不相关的oracle 11g数据库实例。我需要从一个数据库中选择CLOB类型的值,然后使用cx_Oracle使用python将其插入到另一个数据库中。

当我朝一个方向尝试时,结果会是:

Traceback (most recent call last):
  File "<input>", line 1, in <module>
cx_Oracle.DatabaseError: ORA-00942: table or view does not exist

奇怪的是,在另一个方向尝试时,结果却是:

Traceback (most recent call last):
  File "<input>", line 1, in <module>
cx_Oracle.DatabaseError: ORA-00600: internal error code, arguments: [kglslod:subheap], [8], [0x14D9EA270], [], [], [], [], [], [], [], [], []

在同一数据库中选择并插入CLOB值没有问题。

我发现可以使用自定义输出处理程序对其进行修复:

def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
    if defaultType == cx_Oracle.CLOB:
        return cursor.var(cx_Oracle.LONG_STRING, arraysize=cursor.arraysize)

和设置

db_connection.outputtypehandler = OutputTypeHandler

所以我想知道这种行为是设计使然还是其他原因导致的?


这里有一些数据需要测试:

创建表并在database1中插入一行

create table test_table_1
(
    column_1 clob
);

insert into test_table_1 (column_1) values ('test_data_1');

创建表并在database2中插入一行

create table test_table_2
(
    column_2 clob
);

insert into test_table_2 (column_2) values ('test_data_2');

无法从database1提取一个结果并将其插入到database2中的Python脚本

import cx_Oracle


db_1 = cx_Oracle.connect(<user>, <passwd>, <host_db1>)
db_2 = cx_Oracle.connect(<user>, <passwd>, <host_db2>)

cursor_1 = db_1.cursor()
cursor_2 = db_2.cursor()

result = cursor_1.execute("select column_1 from test_table_1")
result1 = result.fetchone()

cursor_2.execute("insert into test_table_2(column_2) values (:val)", val=result1[0])

结果

Traceback (most recent call last):
  File "<input>", line 1, in <module>
cx_Oracle.DatabaseError: ORA-00600: Interner Fehlercode, Argumente: [kglslod:subheap], [8], [0x14DF5A0B0], [], [], [], [], [], [], [], [], []

从database2选择并插入database1时的结果

Traceback (most recent call last):
  File "<input>", line 8, in <module>
cx_Oracle.DatabaseError: ORA-00942: table or view does not exist

有效的Python脚本

import cx_Oracle

def OutputTypeHandler(cursor: cx_Oracle.Cursor, name, defaultType, size, precision, scale):
    """
    Convertes CLOB type to string on reading.
    """
    if defaultType == cx_Oracle.CLOB:
        return cursor.var(cx_Oracle.LONG_STRING, arraysize=cursor.arraysize)

db_1 = cx_Oracle.connect(<user>, <passwd>, <host_db1>)
db_1.outputtypehandler = OutputTypeHandler
db_2 = cx_Oracle.connect(<user>, <passwd>, <host_db2>)

cursor_1 = db_1.cursor()
cursor_2 = db_2.cursor()

result = cursor_1.execute("select column_1 from test_table_1")
result1 = result.fetchone()

cursor_2.execute("insert into test_table_2(column_2) values (:val)", val=result1[0])

db_2.commit()

版本号

  • cx-Oracle 7.2.0
  • oracle_instant_client 18.5(Linux)
  • python 3.6.8

查看更多

提问者
finswimmer
被浏览
20
Anthony Tuininga 2019-07-25 01:07

您不能简单地将LOB值从一个连接传递到另一连接。它们特定于特定连接。我会考虑针对这种情况提供更好的错误消息

正如您在自己的问题中指出的那样,第一个解决方案是通过使用输出类型处理程序来使用STRING而不是CLOB。该技术适用于最大大小为1 GB的CLOB值,但缺点是整个CLOB值必须驻留在连续的内存中。

如果由于长度太长而需要流式传输值,则需要使用如下所示的方法:

cursor_1 = db_1.cursor()
cursor_2 = db_2.cursor()

cursor_1.execute("select column_1 from test_table_1")
sourceLob, = cursor_1.fetchone()

lobVar = cursor_2.var(cx_Oracle.CLOB)
cursor_2.execute("insert into test_table_2 (column_2) values (empty_clob()) returning column_2 into :1", [lobVar])
destLob = lobVar.getvalue()

offset = 1
numBytesInChunk = 65536
while True:
    data = sourceLob.read(offset, numBytesInChunk)
    if data:
        destLob.write(data, offset)
    if len(data) < numBytesInChunk:
        break
    offset += len(data)
db_2.commit()