如何在Python中使用匿名PL/SQL块从Oracle检索查询结果?

2 投票
6 回答
4348 浏览
提问于 2025-04-17 04:43

我找到的所有关于PL/SQL的例子都长得差不多,比如这个(这个例子来自维基百科):

FOR RecordIndex IN (SELECT person_code FROM people_table)
LOOP
  DBMS_OUTPUT.PUT_LINE(RecordIndex.person_code);
END LOOP;

换句话说,它们都有一个共同点:当需要把数据输出到某个地方时,它们都把数据放进了DBMS_OUTPUT,这个地方对应用程序处理数据来说似乎没什么用。

如果我想用PL/SQL的功能从Oracle数据库中获取数据,就像这些数据是普通SQL查询的结果一样,我该怎么做呢?比如说,如果我想处理通过DELETE ... RETURNING ... INTO SQL语句删除的行,跟处理SELECT ... FROM ...的结果一样,该怎么做?

我不想修改数据库的结构,也不想创建任何存储过程;我只想执行cursor.execute("begin; ... something; end"); results = cursor.fetchall()

特别是,我想用cursor.var()创建一个变量,因为这个API在不同的数据库实现之间不太兼容。(显然,SQL本身也不兼容,但大家都知道,不同的数据库后端需要生成定制的SQL字符串。)

6 个回答

1

在Oracle中,“临时表”的概念并不好。它们和SQL*Server或Sybase中的临时表不一样。在Oracle里,表是永久存在的;只有表里的内容是临时的。所以,你不应该写一个需要动态创建或删除表的应用程序——这样做会遇到各种各样的问题。

1

在OCI中,你只能获取简单类型的表,而不能获取记录类型的表。

使用cx_Oracle(Python):

cx = cx_Oracle.connect(dsn)
cu = cx.cursor()
dates = cu.var(cx_Oracle.DATE, 100)
cu.execute("""
DECLARE
  TYPE date_tab_typ IS TABLE OF DATE INDEX BY PLS_INTEGER;
  v_dates date_tab_typ;
BEGIN
  SELECT SYSDATE-ROWNUM BULK COLLECT INTO v_dates
    FROM user_objects
    WHERE ROWNUM < 100;
  :1 := v_dates;
END;
""", [dates])

dates = [dates.getvalue(i+1) for i in xrange(100)]

我写了一个库,可以在存储的函数或过程周围生成这样的匿名PL/SQL块,这样就能使用记录类型的数组(你需要为记录中的每一列创建一个数组)——虽然能用,但不太好。

如果你不能把游标作为返回类型(因为你不能通过选择语句生成记录),那么你可以写PIPELINED存储函数:它们的工作方式像Python的生成器,每次PIPE调用都会产生一条记录!

1

在Oracle 12c中,你可以定义一个临时的PL/SQL函数,并且可以在SELECT语句中使用它:

WITH FUNCTION x(param)
  <body>
END x;
SELECT x(p) FROM t

这其实是一个SQL语句,你可以像平常一样从中获取数据行。不过可惜的是,Oracle 12c还没有正式发布...

撰写回答