【问题标题】:Access cursor by column name dynamically按列名动态访问游标
【发布时间】:2011-06-25 14:17:29
【问题描述】:

我可以动态访问游标的列吗?我是说名字吗?像这样:

declare
 v_cursor := select * from emp;
begin
 FOR reg IN v_cursor LOOP
   dbms_output.put_line(**reg['column_name_as_string']**);
 end loop;
end;

我知道粗体部分 不是 PL/SQL,但我正在寻找类似的东西,但在任何地方都找不到。

谢谢!

【问题讨论】:

    标签: oracle plsql cursor


    【解决方案1】:

    您可以使用包DBMS_SQL 来创建和访问具有动态查询的游标。

    但是,按名称访问列并不简单,因为DBMS_SQL 包使用定位,并且在动态查询中,我们可能不知道执行前列的顺序。

    此外,在这个问题的上下文中,似乎我们可能不知道在编译时要显示哪一列,我们将假设我们要显示的列作为参数给出。

    我们可以在解析SELECT 查询后使用DBMS_SQL.describe_columns 来分析它的列,以构建列的动态映射。我们将假设所有列都可以转换为VARCHAR2,因为我们想用DBMS_OUTPUT 显示它们。

    这是一个例子:

    SQL> CREATE OR REPLACE PROCEDURE display_query_column(p_query VARCHAR2,
      2                                                   p_column VARCHAR2) IS
      3     l_cursor            INTEGER;
      4     l_dummy             NUMBER;
      5     l_description_table dbms_sql.desc_tab3;
      6     TYPE column_map_type IS TABLE OF NUMBER INDEX BY VARCHAR2(32767);
      7     l_mapping_table column_map_type;
      8     l_column_value  VARCHAR2(4000);
      9  BEGIN
     10     l_cursor := dbms_sql.open_cursor;
     11     dbms_sql.parse(l_cursor, p_query, dbms_sql.native);
     12     -- we build the column mapping
     13     dbms_sql.describe_columns3(l_cursor, l_dummy, l_description_table);
     14     FOR i IN 1 .. l_description_table.count LOOP
     15        l_mapping_table(l_description_table(i).col_name) := i;
     16        dbms_sql.define_column(l_cursor, i, l_column_value, 4000);
     17     END LOOP;
     18     -- main execution loop
     19     l_dummy := dbms_sql.execute(l_cursor);
     20     LOOP
     21        EXIT WHEN dbms_sql.fetch_rows(l_cursor) <= 0;
     22        dbms_sql.column_value(l_cursor, l_mapping_table(p_column), l_column_value);
     23        dbms_output.put_line(l_column_value);
     24     END LOOP;
     25     dbms_sql.close_cursor(l_cursor);
     26  END;
     27  /
    
    Procedure created
    

    我们可以使用仅在运行时知道的查询来调用此过程:

    SQL> set serveroutput on
    SQL> exec display_query_column('SELECT * FROM scott.emp WHERE rownum < 5', 'ENAME');
    SMITH
    ALLEN
    WARD
    JONES
    
    PL/SQL procedure successfully completed
    
    SQL> exec display_query_column('SELECT * FROM scott.emp WHERE rownum < 5', 'EMPNO');
    7369
    7499
    7521
    7566
    
    PL/SQL procedure successfully completed
    

    谨慎使用动态 SQL:它具有与用户相同的权限,因此可以执行此架构允许的任何 DML 和 DDL 语句。

    例如,上述过程可用于创建或删除表:

    SQL> exec display_query_column('CREATE TABLE foo(id number)', '');
    begin display_query_column('CREATE TABLE foo(id number)', ''); end;
    ORA-01003: aucune instruction analysée
    ORA-06512: à "SYS.DBMS_SQL", ligne 1998
    ORA-06512: à "APPS.DISPLAY_QUERY_COLUMN", ligne 13
    ORA-06512: à ligne 1
    
    SQL> desc foo
    Name Type   Nullable Default Comments 
    ---- ------ -------- ------- -------- 
    ID   NUMBER Y      
    

    【讨论】:

    • 也许您可以添加 2 行示例。我的意思是,通过“RTFM”回答问题不是示例驱动的方法,因为 stackoverflow 通过 :) 建立了它的声誉
    • @Edgar 我同意,但是用DBMS_SQL 构建一个小例子非常困难,因为它不是非常用户友好。不过,我已经更新了我的答案,希望这会有所帮助:)
    【解决方案2】:

    你的意思是这样的:

    declare
      cursor sel_cur is
      select * from someTable;
    
    begin
      for rec in sel_cur
      loop
        dbms_output.put_line('col1: ' || rec.col1);
      end loop;
    end;
    

    【讨论】:

    • 不,因为 rec.col1 在那里被硬编码。我需要通过列名以编程方式访问rec 的字段。比如:rec['col1'] 或类似的东西。
    【解决方案3】:

    如果可以的话,使查询动态化可能是最简单的。

    DECLARE
      v_cursor  SYS_REFCURSOR;
      dynamic_column_name VARCHAR2(30) := 'DUMMY';
      column_value  VARCHAR2(32767);
    BEGIN
      OPEN v_cursor FOR 'SELECT ' || dynamic_column_name || ' FROM dual';
      LOOP
        FETCH v_cursor INTO column_value;
        EXIT WHEN v_cursor%NOTFOUND;
        dbms_output.put_line( column_value );
      END LOOP;
      CLOSE v_cursor;
    END;
    

    如果您真的想要一个硬编码的SELECT * 并通过名称从其中动态选择一列,我认为您可以按照 Vincent 的建议使用 DBMS_SQL 来做到这一点,但它会稍微复杂一些。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-11
      相关资源
      最近更新 更多