如何使用python-oracledb执行PL/SQL脚本

7fhtutme  于 2023-04-11  发布在  Oracle
关注(0)|答案(1)|浏览(300)

我有一个脚本,到目前为止一直用sqlplus执行,我想用python执行它。我检查了python-oracledb文档,但仍然无法弄清楚。
我尝试做的是这样的事情:

sql = """
DECLARE
v_version            VARCHAR(32);
v_dbname             VARCHAR(32);
v_patch              VARCHAR(32);
v_sql                VARCHAR(255);
BEGIN

SELECT SUBSTR(banner, INSTR(banner, 'Release')+8, 2) INTO v_version FROM v$version WHERE banner LIKE '%Oracle%';
SELECT UPPER(name) INTO v_dbname FROM v$database;

IF v_version > 12 THEN
    v_sql := 'select max(TRIM(REGEXP_SUBSTR(REGEXP_SUBSTR(description,''[^:]+'',1,2),''[^(]+'',1,1))) keep (dense_rank last order by action_time) from registry$sqlpatch';
    EXECUTE IMMEDIATE v_sql INTO v_patch;
    dbms_output.put_line('oracle_sql_patch,db='||v_dbname||' dbver="'||v_patch||'"');
  ELSIF v_version > 11 THEN
    v_sql := 'select max(REGEXP_SUBSTR(REGEXP_SUBSTR(description, ''12.[0-9].*''),''[^(]+'',1,1)) keep (dense_rank last order by action_time) from registry$sqlpatch where bundle_series is not null';
    EXECUTE IMMEDIATE v_sql INTO v_patch;
    dbms_output.put_line('oracle_sql_patch,db='||v_dbname||' dbver="'||v_patch||'"');
  ELSE
    v_sql := 'select max(replace(replace(replace(regexp_replace(comments, ''[^[:digit:].]''),''PSU'',''''),''64'',''''),''2021'','''')) keep (dense_rank last order by action_time) from registry$history';
    EXECUTE IMMEDIATE v_sql INTO v_patch;
    dbms_output.put_line('oracle_sql_patch,db='||v_dbname||' dbver="'||v_patch||'"');
  END IF;
END;"""

cursor.execute(sql)
for implicit_cursor in cursor.getimplicitresults():
    for row in implicit_cursor:
        print(row)

我得到的错误是:

Traceback (most recent call last):
  File "ora_runner.py", line 91, in <module>
    Query(un, pw, cs, False)
  File "ora_runner.py", line 83, in Query
    for implicit_cursor in cursor.getimplicitresults():
  File "/usr/local/lib64/python3.6/site-packages/oracledb/cursor.py", line 551, in getimplicitresults
    return self._impl.get_implicit_results(self.connection)
  File "src/oracledb/impl/thin/cursor.pyx", line 185, in oracledb.thin_impl.ThinCursorImpl.get_implicit_results
  File "/usr/local/lib64/python3.6/site-packages/oracledb/errors.py", line 111, in _raise_err
    raise exc_type(_Error(message)) from cause
oracledb.exceptions.InterfaceError: DPY-1004: no statement executed

启用密集模式是否有帮助?

au9on6nz

au9on6nz1#

您正在接收有问题的错误,因为您正在执行的PL/SQL块不包含任何隐式结果。实际上,您正在尝试读取数据,但没有数据可读取。
我注意到您的PL/SQL块包含对dbms_output.put_line的调用。请放心,调用dbms_output.put_line不会向隐式结果写入任何内容,因此cursor.getimplicitresults()将无法返回以这种方式写入的任何输出。
虽然dbms_output可以方便地在SQLPlus中使用,但在其他情况下就不那么方便了。如果您要求,SQL * Plus(可能还有其他工具)将为您获取并显示这些行。在SQLPlus之外,您必须自己检索这些行。这不是不可能的,可以做到,但只有在您坚持使用dbms_output时才值得这样做,而这里没有。
相反,我建议使用几个OUT绑定变量来返回PL/SQL块中的值。我还删除了对dbms_output.put_line的调用,因为它们没有实现任何功能:

sql = """
DECLARE
v_version            VARCHAR(32);
v_dbname             VARCHAR(32);
v_patch              VARCHAR(32);
v_sql                VARCHAR(255);
BEGIN

SELECT SUBSTR(banner, INSTR(banner, 'Release')+8, 2) INTO v_version FROM v$version WHERE banner LIKE '%Oracle%';
SELECT UPPER(name) INTO v_dbname FROM v$database;

IF v_version > 12 THEN
    v_sql := 'select max(TRIM(REGEXP_SUBSTR(REGEXP_SUBSTR(description,''[^:]+'',1,2),''[^(]+'',1,1))) keep (dense_rank last order by action_time) from registry$sqlpatch';
    EXECUTE IMMEDIATE v_sql INTO v_patch;
  ELSIF v_version > 11 THEN
    v_sql := 'select max(REGEXP_SUBSTR(REGEXP_SUBSTR(description, ''12.[0-9].*''),''[^(]+'',1,1)) keep (dense_rank last order by action_time) from registry$sqlpatch where bundle_series is not null';
    EXECUTE IMMEDIATE v_sql INTO v_patch;
  ELSE
    v_sql := 'select max(replace(replace(replace(regexp_replace(comments, ''[^[:digit:].]''),''PSU'',''''),''64'',''''),''2021'','''')) keep (dense_rank last order by action_time) from registry$history';
    EXECUTE IMMEDIATE v_sql INTO v_patch;
  END IF;
  :bind_dbname := v_dbname;
  :bind_patch := v_patch;
END;"""

bind_dbname = cursor.var(str)
bind_patch = cursor.var(str)
cursor.execute(sql, bind_dbname=bind_dbname, bind_patch=bind_patch)
print(bind_dbname.getvalue())
print(bind_patch.getvalue())

我在我的Oracle 18 cXE数据库上运行了这个命令,最后两行输出了XENone(后者是意料之中的,因为registry$sqlpatch不包含行)。

相关问题