【问题标题】:Return variable from cx_Oracle PL/SQL call in Python从 Python 中的 cx_Oracle PL/SQL 调用返回变量
【发布时间】:2019-05-16 13:55:28
【问题描述】:

我想在 Python 中通过 cx_oracle 执行 Oracle PL/SQL 语句。代码如下所示:

db = cx_Oracle.connect(user, pass, dsn_tns)
cursor = db.cursor()

... 

sel = """
DECLARE
  c   NUMBER := 0.2;
  mn  NUMBER := 1.5;
  res NUMBER;
BEGIN
  res := c+mn/6.;
END;
"""
try:
  cursor.execute(sel) 
  print "PL/SQL successful executed ..."
except cx_Oracle.DatabaseError as e:
  err, = e.args
  print "\n".join([str(err.code),err.message,err.context])

代码运行没有问题,但有没有机会将结果返回给 Python?

【问题讨论】:

    标签: python plsql cx-oracle


    【解决方案1】:

    您可以像这样将输入和输出变量绑定到块。

    import cx_Oracle
    
    SQL_BLOCK = '''
    DECLARE
      v_first   NUMBER;
      v_second  NUMBER;
      v_result  NUMBER;
    BEGIN
      v_first  := :i_first;   -- (1)
      v_second := :i_second;  -- (1)
    
      v_result := (v_first + v_second) / 2;
    
      :o_result := v_result;  -- (1)
    END;
    '''
    
    with cx_Oracle.connect('hr/hr@xe') as db:
        cur = db.cursor()
        o_result = cur.var(cx_Oracle.NUMBER) # (2)
        cur.execute(SQL_BLOCK, i_first=23, i_second=55, o_result=o_result) # (3)
        res = o_result.getvalue()  # (4)
        print('Average of 23 and 55 is: {}'.format(res))
    
    1. 在 PL/SQL 块中为输入和输出变量使用常规绑定表示法 (:)
    2. 对于输出变量,从游标中获取一个变量(适当的类型)
    3. 在执行调用中,为输入变量和 (2) 中的变量提供值作为参数
    4. 从输出变量中检索值

    脚本应该打印出来

    Average of 23 and 55 is: 39.0
    

    【讨论】:

      【解决方案2】:

      你需要一个函数来返回一个结果。匿名块不会。

      你需要在数据库中create a function,例如:

      create or replace function calculation return number is
        c   number := 0.2;
        mn  number := 1.5;
        res number;
      begin
        return c + mn / 6.;
      end;
      /
      

      然后更改您的 Python 代码以调用该函数,使用 callfunc()

      db = cx_Oracle.connect(user, pass, dsn_tns)
      cursor = db.cursor()
      
      try:
        result = cursor.callfunc('calculation', float)
        print result
      except cx_Oracle.DatabaseError as e:
        err, = e.args
        print "\n".join([str(err.code),err.message,err.context])
      

      动态创建函数是不可能的,但您的函数足够简单,您可以在 select 语句中执行它并使用链接文档中描述的 fetchall() 将结果返回给 Python。 fetchall() 返回一个元组列表,因此如果您只在单行和单列之后,您可以立即选择两者的第 0th 索引。

      >>> import cx_Oracle
      >>> db = cx_Oracle.connect('****','****','****')
      >>> cursor = db.cursor()
      >>> SQL = """select 0.2 + 1.5 / 6. from dual"""
      >>> try:
      ...     cursor.execute(SQL)
      ...     result = cursor.fetchall()[0][0]
      ... except cx_Oracle.DataBaseError, e:
      ...     pass
      ...
      <__builtin__.OracleCursor on <cx_Oracle.Connection to ****@****>>
      >>> result
      0.45000000000000001
      >>>
      

      您还可以使用绑定变量将变量传递到您的 execute() 调用中,因此如果需要,可以在 Python 中实例化它们:

      >>> c = 0.2
      >>> mn = 1.5
      >>> SQL = """select :c + :mn / 6. from dual"""
      >>> bind_vars = { 'c' : c, 'mn' : mn }
      >>> cursor.execute(SQL, bind_vars)
      <__builtin__.OracleCursor on <cx_Oracle.Connection to history@monitor>>
      >>> result = cursor.fetchall()[0][0]
      >>> result
      0.45000000000000001
      >>>
      

      虽然在 Python 中执行所有这些操作可能更简单...我假设您的实际情况更复杂?

      【讨论】:

      • 是否可以在调用 db.close() 后以数据库忘记它的方式临时“即时”创建函数?
      • 表格“dual”有什么意义还是某种占位符?
      • 只是一种占位符@user1946052;您不能在 Oracle 中从无中选择:请参阅 docs.oracle.com/cd/E11882_01/server.112/e25789/…
      • 我需要同样的东西,实际上找到了下面的解决方案。没有必要仅仅为了执行一些 PL/SQL 并检索结果而定义一个函数。
      【解决方案3】:

      同意 M. Wymann,我需要通过传递列表中的元素数量来返回一个字符串。这是我的代码。

      cursor = con.cursor()    
          for a,b,c in data:
          statement='''DECLARE
           t_name     VARCHAR2 (50);
           owner      VARCHAR2 (50);
           c_name     VARCHAR2 (50);
           O_type     VARCHAR2 (50);
           nullable   VARCHAR2 (20);
      BEGIN
         SELECT t1.table_name,
                t1.owner,
                t1.column_name,
                CASE
                   WHEN t1.data_type = 'NUMBER' AND t1.data_precision IS NULL
                   THEN
                      'NUMBER'
                   WHEN t1.data_type = 'DATE'
                   THEN
                      t1.data_type
                   WHEN REGEXP_REPLACE (t1.data_type, '(\d)|VAR', '') = 'CHAR'
                   THEN
                      t1.data_type || '(' || t1.DATA_LENGTH || ')'
                   WHEN     t1.data_type = 'NUMBER'
                        AND t1.data_precision IS NOT NULL
                        AND t1.data_scale = 0
                   THEN
                      'NUMBER(' || t1.data_precision || ')'
                   WHEN     t1.data_type = 'NUMBER'
                        AND t1.data_precision IS NOT NULL
                        AND t1.data_scale <> 0
                   THEN
                      'NUMBER(' || t1.data_precision || ',' || t1.data_scale || ')'
                   ELSE
                      'Not Handled'
                END
                   "Oracle data type",
                t1.nullable
           INTO t_name,
                owner,
                c_name,
                O_type,
                nullable
           FROM all_tab_columns t1
          WHERE     t1.table_name = :tname
                AND t1.owner = :towner
                AND t1.column_name = :tcolname;
      
         :o_result :=
               t_name
            || '|'
            || owner
            || '|'
            || c_name
            || '|'
            || O_type
            || '|'
            || nullable;
      EXCEPTION
         WHEN OTHERS
         THEN
            t_name := :tname;
            c_name := 'NOT FOUND ';
            owner := :towner;
            O_type := 'NOT FOUND ';
            nullable := 'NOT FOUND ';
            :o_result :=
                  t_name
               || '|'
               || owner
               || '|'
               || c_name
               || '|'
               || O_type
               || '|'
               || nullable;
      END;'''
          o_result = cursor.var(cx_Oracle.STRING)`enter code here`
          cursor.execute(statement, tname=a, towner=b, tcolname=c, o_result=o_result)
          ObLst = o_result.getvalue().split('|')
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-21
        • 1970-01-01
        • 2022-08-12
        • 2011-03-01
        • 1970-01-01
        • 2017-03-30
        • 2015-02-21
        • 1970-01-01
        相关资源
        最近更新 更多