【问题标题】:Is there a way to grab a CURSOR type column with DBMS_SQL?有没有办法用 DBMS_SQL 获取 CURSOR 类型的列?
【发布时间】:2018-07-20 15:36:23
【问题描述】:

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

所以我在想:如果尝试更动态地处理这个问题呢?
游标语句将允许我只要求用户在界面提供的一个语句框中编写他的查询(如果您知道 apex,区域源 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 的代码失败。当然。接受 CURSOR 类型作为 DBMS_SQL 中的类型没有重载。

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 oracle plsql cursor


    【解决方案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));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-25
      • 1970-01-01
      • 1970-01-01
      • 2010-11-05
      相关资源
      最近更新 更多