【问题标题】:inserting CLOB to database selected from another将 CLOB 插入从另一个数据库中选择的数据库
【发布时间】:2019-12-02 06:48:16
【问题描述】:

我有两个不相关的 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');

Python 脚本未能从 database1 获取一个结果并将其插入 database2

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

【问题讨论】:

  • 你能分享一个完全可运行的测试用例来展示问题,这样我们就不必猜测了吗?确保您使用的是最新的 cx_Oracle 版本。最新的是 7.2。
  • 我添加了一个测试用例。

标签: python oracle cx-oracle


【解决方案1】:

您不能简单地将 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()

【讨论】:

  • 这就是我所说的“这是设计使然吗?”。这是因为 oracle 只是在表中存储了对 clob 存储的引用吗?更好的错误消息确实很好。 :) 我花了几个小时才找到问题。
  • Oracle 客户端库不支持该概念,因此我猜您可能会认为这是“设计使然”。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 2015-09-22
  • 2014-11-21
  • 1970-01-01
  • 2014-04-18
  • 1970-01-01
相关资源
最近更新 更多