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

时间:2018-02-09 19:46:09

标签: sql oracle plsql cursor

一些上下文:我正在考虑以某种方式创建一个顶点插件,非常具体但可以在同一个项目中重用。我只是通过研究我的选择来解决这个问题。我并不真的需要它,因为refcursor将满足我的需求,因为我总是期望一个具有相同列名和数据类型的语句。问题是可以在不同的数据集上执行查询(想想包含定义的表和包含用户数据的表)。

所以我在想:如果试图处理这种更具动力性的问题呢? 游标语句允许我只是要求用户在接口提供的一个语句框中写入他的查询(如果你知道顶点,区域源sql)并且没有拆分或使其复杂化。但我对如何应对这一点感到有些困惑。似乎没有办法解决这个问题。例如,使用以下语句,可以描述查询并确定列是否为CURSOR类型。但它停在那里。没有办法抓住这个实际的光标并用它做一些事情。

一些测试表+数据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>,}>,}

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

1 个答案:

答案 0 :(得分:1)

SQL Developer就是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));
    }
}