2

All the examples of using PL/SQL I can find end up looking like this (this example taken from Wikipedia):

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

In other words, they all have one thing in common: when it comes time to actually output the data somewhere, they stick it in DBMS_OUTPUT, which seems to be a pretty useless place for an application to process it.

If I wanted to retrieve data from an Oracle database using a PL/SQL feature as if this data were the result of a plain SQL query, how would I do it? For example, if I wanted to process the rows deleted by a DELETE ... RETURNING ... INTO SQL statement in the same way that I would process the results of SELECT ... FROM ...?

I do not want to modify the schema of the database or create any stored procedures; I'd just like to do cursor.execute("begin; ... something; end"); results = cursor.fetchall().

Particularly, I do not want to create a variable with cursor.var(), since that API is not portable between database implementations. (Obviously the SQL won't be portable either, but it's a generally accepted fact that one needs to generate custom SQL strings for different database backends no matter what.)

4

6 回答 6

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 调用生成一条记录!

于 2013-04-19T17:59:39.100 回答
1

使用 Oracle 12c,您将能够定义一个临时 PL/SQL 函数在 SELECT 语句中使用它:

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

这是一个SQL 语句,一个可以以通常方式从中获取行的选择。不幸的是,Oracle 12c 还没有发布......

于 2013-04-18T23:28:20.503 回答
1

Oracle 中的“临时表”想法很糟糕。它们不像 SQL*Server 或 Sybase 中的临时表。在 Oracle 中,表是 PERMANENT;只有内容是临时的。所以你不应该编写一个需要动态创建/删除表的应用程序——你会遇到这种方法的各种问题。

于 2013-04-19T09:23:39.543 回答
0

您的要求是:

  • 我不想修改数据库的架构
  • 或创建任何存储过程......特别是,我不想用 cursor.var() 创建一个变量 (不幸的是,存储过程是通常的方法)

您还在评论中询问了如何创建一个将在事务结束时销毁的临时表,我会给您一个示例(因为我认为这是满足您要求的唯一方法) .

[关于这不处理错误或防弹的通常警告,但如果你真的想以这种方式继续,这应该让你开始]

import cx_Oracle

def main():
    block = """
        BEGIN
            -- you could put your PL/SQL in here, 
            -- to insert values into this table
            INSERT INTO some_table (col1, col2) VALUES ('test', 'this');
        END;
    """

    conn = cx_Oracle.connect("User/password@somewhere")
    curs = conn.cursor()
    conn.begin()
    curs.execute("""CREATE GLOBAL TEMPORARY TABLE some_table (
                        col1 VARCHAR2(10), 
                        col2 VARCHAR2(10)
                    ) ON COMMIT PRESERVE ROWS""")
    curs.execute(block)
    curs.execute('SELECT * FROM some_table')
    print(curs.fetchall())
    curs.execute('TRUNCATE TABLE some_table')
    curs.execute('DROP TABLE some_table')
    conn.commit()

if __name__ == '__main__':
    main()

返回:[('test', 'this')]

Oracle全局临时表文档在这里。

于 2013-04-19T00:52:57.827 回答
0

您可以在匿名块中使用 DBMS_SQL 包来描述您的查询

    q="""DECLARE
  c           NUMBER;
  d           NUMBER;
  col_cnt     INTEGER;
  f           BOOLEAN;
  rec_tab     DBMS_SQL.DESC_TAB;
  col_num    NUMBER;
  v_sql dbms_sql.varchar2a;
 v_sql_1 varchar2(32767);
 v_sql_2 varchar2(32767);
 v_sql_3 varchar2(32767);
 v_sql_4 varchar2(32767);
  v_type VARCHAR2(32):='';
  PROCEDURE print_rec(rec in DBMS_SQL.DESC_REC) IS
  BEGIN
    v_type:=CASE rec.col_type
                WHEN 1 THEN 'VARCHAR2'
                WHEN 12 THEN 'DATE'
                WHEN 2 THEN 'NUMBER'
            ELSE ''||rec.col_type
            END;
    DBMS_OUTPUT.PUT_LINE(rec.col_name||':'||rec.col_max_len||':'||v_type);
  END;
BEGIN
  v_sql(1):='%s';
  v_sql(2):='%s';
  v_sql(3):='%s';
  v_sql(4):='%s';
  v_sql(5):='%s';
  c := DBMS_SQL.OPEN_CURSOR;
  DBMS_SQL.PARSE(c, v_sql,1,5,False, DBMS_SQL.NATIVE);
  d := DBMS_SQL.EXECUTE(c);
  DBMS_SQL.DESCRIBE_COLUMNS(c, col_cnt, rec_tab);
/*
 * Following loop could simply be for j in 1..col_cnt loop.
 * Here we are simply illustrating some of the PL/SQL table
 * features.
 */
  col_num := rec_tab.first;
  IF (col_num IS NOT NULL) THEN
    LOOP
      print_rec(rec_tab(col_num));
      col_num := rec_tab.next(col_num);
      EXIT WHEN (col_num IS NULL);
    END LOOP;
  END IF;
  DBMS_SQL.CLOSE_CURSOR(c);
END;
/
""" % (qry[0:32000].replace("'","''"),qry[32000:64000].replace("'","''"),qry[64000:96000].replace("'","''"),qry[96000:128000].replace("'","''"),qry[128000:160000].replace("'","''"))
    regexp=re.compile(r'([\w\_\:\(\)\d]+)')

在这一步之后,您可以创建 SQL 语句并使用 SQL*Plus 运行提取

检查extractor.py以获取更多详细信息。

于 2016-05-18T12:44:07.787 回答
-1

您可以通过 2 种方式做到这一点: 1. 不可移植 - 写入全局上下文。2. 便携式写入临时表。

于 2011-12-15T09:52:18.540 回答