【问题标题】:cx_Oracle How to Pass a Composite/Complex array to a PLSQL Procedure?cx_Oracle 如何将复合/复杂数组传递给 PLSQL 过程?
【发布时间】:2017-10-03 07:39:28
【问题描述】:

通过这个guide,我能够将一个简单数据类型(如cx_Oracle.NUMBER)的关联数组传递给PL/SQL 过程。

CREATE OR REPLACE PACKAGE test
IS
    TYPE t_ids IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
    PROCEDURE foo(p_ids_i IN t_ids);
END;
/

称呼它:

ids = cursor.arrayvar(cx_Oracle.NUMBER, [1,2,3])
cursor.callproc('test.foo', [ids])

但是,我想调用下面的过程 foo,而不是采用复杂类型。

CREATE OR REPLACE PACKAGE test
IS
    TYPE r_foo IS RECORD (id NUMBER, name VARCHAR2(10));
    TYPE t_complex IS TABLE OF r_foo INDEX BY PLS_INTEGER;
    PROCEDURE foo(p_ids_i IN t_complex);
END;
/

我尝试了各种方法,例如:

# Raises NotSupportedError: Variable_TypeByPythonType(): unhandled data type
foos = cursor.arrayvar((cx_Oracle.NUMBER, cx_Oracle.STRING), [(1, 'foo'), (2, 'bar')])

# Raises NotSupportedError: Variable_MakeArray(): type does not support arrays
foos = cur.arrayvar(cx_Oracle.OBJECT, [(1, 'foo'), (2, 'bar')])

以下失败:

# Raises DatabaseError: ORA-04043: object TEST.R_FOO does not exist
record_type = conn.gettype('TEST.R_FOO')

看起来您可以在包之外创建一个类型并引用它。

CREATE TYPE t_foo IS TABLE OF NUMBER; -- Not an Associative Array

引用它:

t = conn.gettype('T_FOO')

但是,您不能在包之外创建关联数组的记录。我可以用 Object 替换 RECORD,但我想不出任何东西来替换 Associative Array,这是 cx_Oracle 可以传入或传出的唯一集合类型。


完整代码:

PL/SQL:

-- 返回 12.1.0.2.0 从 v$instance 中选择版本; 创建或替换包测试 是 TYPE r_foo IS RECORD (id NUMBER, name VARCHAR2(10)); 类型 t_complex 是 PLS_INTEGER 的 r_foo 索引表; 程序 foo(p_ids_i IN t_complex); 结尾; / 创建或替换包体测试 是 程序 foo(p_ids_i IN t_complex) 是 开始 FOR i IN p_ids_i.FIRST .. p_ids_i.LAST LOOP DBMS_OUTPUT.PUT_LINE(p_ids_i(i).id || ' ' || p_ids_i(i).name); 结束循环; 结尾; 结尾; / - 以下工作按预期工作。 宣布 l_complex test.t_complex; 开始 l_complex(1).id := 1; l_complex(1).name := '马修'; l_complex(2).id := 2; l_complex(2).name := 'Moisen'; test.foo(l_complex); 结尾;

Python:

导入 cx_Oracle 打印 cx_Oracle.version # 5.3 print cx_Oracle.clientversion() # (12, 1, 0, 2, 0) conn = cx_Oracle.connect('用户名/密码@sid') cur = conn.cursor() 结果 = cur.execute("SELECT * FROM user_source WHERE name = 'TEST' and type = 'PACKAGE'") # 这将成功打印包规范 结果中的行: 打印行 # 引发 DatabaseError: ORA-04043: 对象 TEST.R_FOO 不存在 conn.gettype('TEST.R_FOO') # 引发 DatabaseError: ORA-04043: 对象 TEST.T_COMPLEX 不存在 conn.gettype('TEST.T_COMPLEX') # 这会引发适当的异常,说我调用了该过程 # 错误,表明我可以访问它。 cur.callproc('TEST.FOO', [])

在将 $ORACLE_HOME 等设置为我的 12c 客户端重新安装 cx_Oracle 后,我能够更进一步,但仍然遇到 append 操作错误。

导入 cx_Oracle conn = cx_Oracle.connect('用户名/密码@sid') # 这不再引发错误 recordTypeObj = conn.gettype('TEST.R_FOO') tableTypeObj = conn.gettype('TEST.T_COMPLEX') rec = recordTypeObj.newobject() 选项卡 = tableTypeObj.newobject() # 这很好用 记录 ID = 1 rec.NAME = "foo" # 这失败了 # cx_Oracle.NotSupportedError: Object_ConvertFromPython(): 未处理的数据类型 250 tab.append(rec)

【问题讨论】:

    标签: python oracle plsql cx-oracle


    【解决方案1】:

    cx_Oracle 5.3 及更高版本支持此功能。你必须使用支持这种东西的“对象”语法。

    import cx_Oracle
    
    conn = cx_Oracle.Connection("cx_Oracle/dev@localhost/orcl")
    recordTypeObj = conn.gettype("TEST.R_FOO")
    tableTypeObj = conn.gettype("TEST.T_COMPLEX")
    
    tab = tableTypeObj.newobject()
    
    rec = recordTypeObj.newobject()
    rec.ID = 1
    rec.NAME = "foo"
    tab.append(rec)
    
    rec = recordTypeObj.newobject()
    rec.ID = 2
    rec.NAME = "bar"
    tab.append(rec)
    
    cursor = conn.cursor()
    cursor.callproc("test.foo", [tab])
    

    【讨论】:

    • the release notes for 5.3 中写着Added support for creating, modifying and binding PL/SQL records and collections (available in Oracle 12.1). 你知道这是否是你所指的注释,这是否意味着它不适用于11g?
    • 是的。这在 Oracle 11g 中不受支持。
    • conn.gettype('TEST.R_FOO') 正在抛出 DatabaseError: ORA-04043: object test.r_foo does not exist。似乎 cx_Oracle 没有在包中找到任何类型;而它可以在包之外找到一种类型。你介意看看我更新的问题吗?
    • 这表明您使用的是不支持的 Oracle 11g。您使用的是 12c 客户端和服务器吗?
    • 抱歉耽搁了。我使用的是 11G,但我现在已经升级到 12CR1,但我仍然收到同样的错误。 cx_Oracle.version 的输出为5.3cx_Oracle.clientversion() 的输出为(12, 1, 0, 2, 0)。关于它为什么不起作用的任何想法?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-21
    • 2014-04-01
    • 2014-02-19
    • 2021-10-19
    相关资源
    最近更新 更多