有没有办法用 DBMS_SQL 获取 CURSOR 类型的列?

Is there a way to grab a CURSOR type column with DBMS_SQL?

一些背景:我正在考虑以某种方式创建一个 apex 插件,非常具体但可在同一个项目中重复使用。我通过稍微研究一下我的选择就得出了这个问题。我真的不需要它,因为 refcursor 将满足我的需求,因为我总是希望有一个具有相同列名和数据类型的语句。问题是查询可以在不同的数据集上执行(想想 tables 的定义和 tables 的用户数据)。

所以我在想:如果尝试更动态地处理这个问题怎么办?
游标语句允许我只要求用户在界面提供的一个语句框中写下他的查询(如果你知道顶点,区域源 sql),而不是把事情分开或让它变得复杂.但我对如何处理这个问题有点难过。似乎没有任何方法可以解决这个问题。例如,使用以下语句可以描述查询并确定列是否为 CURSOR 类型。但它停在那里。没有办法抓住这个实际的光标并对它做些什么。

一些测试table+数据DDL

create table test_a (id number, title varchar2(200))

create table test_b (id number, a_id number, title varchar2(200));

create table test_c (id number, b_id number, title varchar2(200));

insert into test_a (id, title) values (1, 'hoofd 1');
insert into test_a (id, title) values (2, 'hoofd 2');

insert into test_b (id, a_id, title) values (1, 1, 'h1 - child 1');
insert into test_b (id, a_id, title) values (2, 1, 'h1 - child 2');
insert into test_b (id, a_id, title) values (3, 2, 'h2 - child 1');
insert into test_b (id, a_id, title) values (4, 2, 'h2 - child 2');

insert into test_c (id, b_id, title) values (1, 1, 'h1 - c1 - A');
insert into test_c (id, b_id, title) values (2, 2, 'h1 - c2 - B');
insert into test_c (id, b_id, title) values (3, 3, 'h2 - c1 - C');
insert into test_c (id, b_id, title) values (4, 4, 'h2 - c2 - D');

清理它:

drop table test_a;
drop table test_b;
drop table test_c;

就像,我明白为什么下面的工作,但正如你所看到的,它需要游标的内容和代码应该处理它的知识。

declare
  l_stmt  varchar2(32000);
  l_c     sys_refcursor;
  l_vc    varchar2(4000);
  l_sub   sys_refcursor;

  procedure children (p_c in sys_refcursor)
  is
    l_k varchar2(200);
    l_c sys_refcursor;
  begin
    loop
      fetch p_c into l_k, l_c;
      exit when p_c%NOTFOUND;
      dbms_output.put_line('L2 titel: '||l_k);
    end loop;
  end;

begin
  l_stmt := q'[select title
, cursor(
    select title, cursor(
      select title
      from test_c
      where b_id = b.id
    ) 
    from test_b b 
    where a_id = a.id
  )
from test_a a]';

  open l_c for l_stmt;

  loop
    fetch l_c into l_vc, l_sub;
    exit when l_c%NOTFOUND;
    dbms_output.put_line('L1 titel: '||l_vc);
    children(l_sub);
  end loop;

  close l_c;
end;
/

以下带有 DBMS_SQL 的代码失败。当然。在 DBMS_SQL.

中没有接受 CURSOR 类型作为类型的重载
set serveroutput on
declare
  l_stmt       VARCHAR2(32000);
  l_cur_id     BINARY_INTEGER;
  l_rows       INTEGER;
  l_desctab    DBMS_SQL.DESC_TAB3;
  l_colcnt     NUMBER;
  l_count      BINARY_INTEGER := 0;

  l_varchar    VARCHAR2(4000);
  l_sub        SYS_REFCURSOR;
begin
l_stmt := q'[select title
, cursor(
    select title, cursor(
      select title
      from test_c
      where b_id = b.id
    ) 
    from test_b b 
    where a_id = a.id
  )
from test_a a]';

  l_cur_id := dbms_sql.open_cursor;
  dbms_sql.parse(l_cur_id, l_stmt, dbms_sql.native);
  dbms_sql.describe_columns3(l_cur_id, l_colcnt, l_desctab);

  FOR i IN 1 .. l_colcnt LOOP
  dbms_output.put_line('col '||i||' type: '||l_desctab(i).col_type);
  END LOOP;
  -- describing it works, and would return type id 102, which I suppose is CURSOR.

  dbms_sql.define_column(l_cur_id, 1, l_varchar, 4000);
  dbms_sql.define_column(l_cur_id, 2, l_sub); --> fails, evidently

  --l_rows := dbms_sql.execute(l_cur_id);

  dbms_sql.close_cursor(l_cur_id);
EXCEPTION
  WHEN OTHERS THEN
  dbms_output.put_line('error: '||sqlerrm);
  dbms_sql.close_cursor(l_cur_id);
END;
/

如果没有办法,那也没关系。在学术上,我只是想知道。也许还有其他方法可以做到这一点。也许我错过了什么。我在想,也许组装 plsql 代码并动态执行它,但这听起来太过分了。就像通过查找游标语句手动解析语句,我就不搞了。
顺便说一句,在 sqldeveloper 中执行查询工作正常,它将显示以下内容:

hoofd 1 {<TITLE=h1 - child 1,CURSOR(SELECTTITLE={<TITLE=h1 - c1 - A>,}>,<TITLE=h1 - child 2,CURSOR(SELECTTITLE={<TITLE=h1 - c2 - B>,}>,}
hoofd 2 {<TITLE=h2 - child 1,CURSOR(SELECTTITLE={<TITLE=h2 - c1 - C>,}>,<TITLE=h2 - child 2,CURSOR(SELECTTITLE={<TITLE=h2 - c2 - D>,}>,}

当然,这可能完全出于其他原因。

SQL 开发人员就是全部 java 这就是我们可以这样做的原因。基本上,在 java 中,游标表达式的结果作为 ResultSet 呈现给我们。这与任何 sql 执行的呈现方式相同。因此,从纯粹的 java 立场来看,这是一个普通的结果,所有正常的 java API 都可以正常工作。

现在 dbms_sql,目前还没有等效项。

仅供参考,这就是 java 的样子

Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@//localhost:1521/XE","klrice","klrice");
PreparedStatement stmt = conn.prepareStatement("select table_name\n" + 
        ", cursor(\n" + 
        "        select column_name\n" + 
        "      from user_tab_columns cols\n" + 
        "      where cols.table_name = tabs.table_name \n" + 
        "  )\n" + 
        "from user_tables tabs");
ResultSet rset = stmt.executeQuery();
while( rset.next() ) {

    // get the first column
    System.out.println(rset.getString(1));


    // get column from cursor expression
    // cast it to a ResultSet
    ResultSet cursor = (ResultSet)  rset.getObject(2);
    while(cursor.next() ) {
        System.out.println("\t"+cursor.getString(1));
    }
}