在运行时访问Oracle PLSQL记录类型的元素

问题描述 投票:1回答:3

我正在使用动态SQL,我动态地使用列名的值来绑定,并将其值绑定

旧代码

<Outer Loop>
FOR i IN lvaDBOBJDTLRecTab.FIRST .. lvaDBOBJDTLRecTab.LAST
LOOP
  DBMS_SQL.BIND_VARIABLE ( lvnInsertCursorId, ':RTTEXT2VC100', 
  lvaDBOBJDTLRecTab(i).DBONAME  );

  DBMS_SQL.BIND_VARIABLE ( lvnInsertCursorId, ':RTTEXT3VC100', 
  lvaDBOBJDTLRecTab(i).DBOTYPE  );
    3.
   .
  .
  .
  100

END LOOP;

我想要动态访问集合的值,而不是写BIND_VARIABLE 100次。我能够动态获取列的值,需要绑定(lvsColForBinding),但是lvsColValForBind的值是'lvrCurDBOBJDTL(i).DBONAME','lvrCurDBOBJDTL(i).DBOTYPE',其余部分也是如此。 98列,

       <Inner Loop>
    FOR j IN lvaMappingTab.FIRST..lvaMappingTab.LAST
    LOOP
    lvsColForBinding := ':'||lvaMappingTab(j).MstRptColCds;

        lvsColValForBind :=  'lvrCurDBOBJDTL(i).'||lvaMappingTab(j).RptColCd;

DBMS_SQL.BIND_VARIABLE ( lvnInsertCursorId,lvsColForBinding, lvsColValForBind);

  END LOOP; 

当为每一行运行DBMS_SQL.BIND_VARIABLE时,如前所述,要绑定的列是正确的但是要绑定的值,而不是作为'XYZ'的值'lvrCurDBOBJDTL(i).DBONAME它在单引号'lvrCurDBOBJDTL中出现(i).DBONAME'对所有列都相同。我们如何在内循环中提取每个元素的值。我们需要采取什么步骤来获取lvsColValForBind的值?

在通过SQLDEveloper Watches进行调试时,我可以看到,在添加和双击plsql记录变量时,元素名称,值和类型,后面的SQL是什么,我们可以在编码中使用它吗?

oracle plsql dynamic-sql
3个回答
0
投票

当您调用bind_variable时,您将实际值绑定到占位符。因此,如果您提供的字符串是您的变量的名称,那么该字符串是绑定到占位符的值。

如果数组包含这些值,则只需引用数组元素而不是该元素的名称,如下所示:

DBMS_SQL.BIND_VARIABLE ( 
   lvnInsertCursorId,
   lvaMappingTab(j).MstRptColCds, 
   lvrCurDBOBJDTL(i).lvaMappingTab(j).RptColCd);

但我很确定那不是你所拥有的。希望这可以帮助!


0
投票

我的第一个建议是使用动态SQL生成大量哑代码,而不是使用少量的智能PL / SQL。如果代码生成不起作用,您可以使用ANYDATA和ANYTYPE创建PL / SQL反射,以在运行时动态迭代记录的元素。

代码生成

不要写BIND_VARIABLE 100次,而是创建一个小程序来为你生成100行代码。如果数据最终来自一个表并进入另一个表,则输入和输出可以基于数据字典视图(如DBA_TAB_COLUMNS)进行预测。

希望像这样的小查询可以帮助生成单个表的所有代码:

--Generate PL/SQL statements for binds.
select
    'DBMS_SQL.BIND_VARIABLE(lvnInsertCursorId, '':RTTEXT'||column_id||'VC100'', lvaDBOBJDTLRecTab(i).'||column_name||');'
from dba_tab_columns
where owner = 'SOME_OWNER'
    and table_name = 'SOME_TABLE'
order by 1;

然后,您可以将输出复制并粘贴到PL / SQL块中。您可能还需要一个警告,例如“不要修改,此代码由CODE_TRON_2000过程自动生成”。

只有在PL / SQL代码可预测的情况下,此方法才能生效,具体取决于数据字典或其他一些元数据。

PL / SQL反射

对于PL / SQL类型没有纯粹的PL / SQL反射*但是如果您愿意将记录类型创建为SQL对象,则会有一个简单的解决方法。如果所有PL / SQL记录都基于对象类型,则可以使用ANYDATA和ANYTYPE动态访问属性。对象类型和PL / SQL记录类型非常相似,将一个转换为另一个应该相对轻松。

例如,如果创建包含数字和字符串的对象类型:

create or replace type v_type is object(a number, b varchar2(1));

这个(痛苦的)PL / SQL块显示了如何遍历集合的所有记录,然后遍历每个记录中的所有属性。 (代码打印的值,你必须自己添加绑定部分。)

declare
    type v_nt_type is table of v_type;
    v_values v_nt_type := v_nt_type(v_type(1, 'A'), v_type(2, 'B'));
begin
    --For each record:
    for i in 1 .. v_values.count loop
        declare
            v_anydata anydata := anydata.ConvertObject(v_values(i));
            v_number number;
            v_varchar2 varchar2(4000);
            v_result pls_integer;
            v_anytype anytype;
            v_dummy_num  pls_integer;
            v_dummy_char varchar2(4000);
            v_dummy_anytype anytype;
            v_number_of_elements number;
        begin
            --Get the ANYTYPE and the number of elements.
            v_result := v_anydata.getType(v_anytype);
            v_result := v_anytype.getInfo
            (
               prec        => v_dummy_num,
               scale       => v_dummy_num,
               len         => v_dummy_num,
               csid        => v_dummy_num,
               csfrm       => v_dummy_num,
               schema_name => v_dummy_char,
               type_name   => v_dummy_char,
               version     => v_dummy_char,
               numelems    => v_number_of_elements
            );

            --For each element in the record:
            for i in 1 .. v_number_of_elements loop
                --Find the type of the element:
                v_anydata.piecewise;
                v_result := v_anytype.getAttrElemInfo(
                pos            => i,
                prec           => v_dummy_num,
                scale          => v_dummy_num,
                len            => v_dummy_num,
                csid           => v_dummy_num,
                csfrm          => v_dummy_num,
                attr_elt_type  => v_dummy_anytype,
                aname          => v_dummy_char);

                --This is where you do something interesting with the values.
                --(The same code merely prints the values.)
                if v_result = dbms_types.typecode_number then
                    v_result := v_anydata.getNumber(num => v_number);
                    dbms_output.put_line(v_number);
                elsif v_result = dbms_types.typecode_varchar2 then
                    v_result := v_anydata.getVarchar2(c => v_varchar2);
                    dbms_output.put_line(v_varchar2);
                --TODO: Add other potential types here.
                end if;
            end loop;
        end;
    end loop;
end;
/

结果:

1
A
2
B

*你是对的,如果调试器得到它,必须有一些方法来找到这个运行时信息。但据我所知,PL / SQL无法检索调试信息。也许它只适用于OCI(?)界面?


0
投票

@Jon感谢您的投入,这有所帮助。我也可以使用DBMS_SQL.DESCRIBE_COLUMNS迭代cols而无需创建OBJECT。

**下面的代码仍然需要一点微调,但大多数工作:)

   BEGIN
        COLS_TRAVERSE('SELECT * FROM ALL_OBJECTS WHERE ROWNUM<=100');
   END;



   create or replace PROCEDURE COLS_TRAVERSE ( p_query in varchar2 )
   AS
            v_curid    NUMBER;
            v_desctab  DBMS_SQL.DESC_TAB;
            v_colcnt   NUMBER;
            v_RowNumcnt   NUMBER := 1;
            v_Colname_var  VARCHAR2(10000);
            v_name_var  VARCHAR2(10000);
            v_num_var   NUMBER;
            v_date_var  DATE;
            v_row_num    NUMBER;
            p_sql_stmt VARCHAR2(1000);

   BEGIN
        v_curid := DBMS_SQL.OPEN_CURSOR;
        DBMS_SQL.PARSE(v_curid, p_query, DBMS_SQL.NATIVE);
        DBMS_SQL.DESCRIBE_COLUMNS(v_curid, v_colcnt, v_desctab);

       -- Define columns:
       FOR i IN 1 .. v_colcnt LOOP
        IF v_desctab(i).col_type = 2 THEN
            DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_num_var);
            ELSIF v_desctab(i).col_type = 12 THEN
            DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_date_var);
            ELSE
            DBMS_SQL.DEFINE_COLUMN(v_curid, i, v_name_var, 50);
            END IF;
        END LOOP;
        v_row_num := dbms_sql.execute(v_curid);
        -- Fetch rows with DBMS_SQL package:
        WHILE DBMS_SQL.FETCH_ROWS(v_curid) > 0 LOOP

          FOR i IN 1 .. v_colcnt 

            LOOP
                v_Colname_var := v_desctab(i).col_name;
                dbms_output.put_line( 'Name:' ||v_Colname_var );
                IF (v_desctab(i).col_type = 1) THEN
                    DBMS_SQL.COLUMN_VALUE(v_curid, i, v_name_var);
                    dbms_output.put_line( 'String Value:' || v_name_var );
                ELSIF (v_desctab(i).col_type = 2) THEN
                    DBMS_SQL.COLUMN_VALUE(v_curid, i, v_num_var);
                    dbms_output.put_line( 'Number Value:' || v_num_var);
                ELSIF (v_desctab(i).col_type = 12) THEN
                    DBMS_SQL.COLUMN_VALUE(v_curid, i, v_date_var);
                    dbms_output.put_line( 'Date Value:' || v_date_var );
                END IF;
            END LOOP;



            dbms_output.put_line( 'End of Row Number # ' ||v_RowNumcnt );

            v_RowNumcnt := v_RowNumcnt+1;

        END LOOP;

        DBMS_SQL.CLOSE_CURSOR(v_curid);
     END;
     /


    DBMS_OUT PUT 

    Name:OWNER
    String Value:SYS
    Name:OBJECT_NAME
    String Value:ORA$BASE
    Name:SUBOBJECT_NAME
    String Value:
    Name:OBJECT_ID
    Number Value:134
    Name:DATA_OBJECT_ID
    Number Value:
    Name:OBJECT_TYPE
    String Value:EDITION
    Name:CREATED
    Date Value:30-03-18
    Name:LAST_DDL_TIME
    Date Value:30-03-18
    Name:TIMESTAMP
    String Value:2018-03-30:21:37:22
    Name:STATUS
    String Value:VALID
    Name:TEMPORARY
    String Value:N
    Name:GENERATED
    String Value:N
    Name:SECONDARY
    String Value:N
    Name:NAMESPACE
    Number Value:64
    Name:EDITION_NAME
    String Value:
    Name:SHARING
    String Value:NONE
    Name:EDITIONABLE
    String Value:
    Name:ORACLE_MAINTAINED
    String Value:Y
    Name:APPLICATION
    String Value:N
    Name:DEFAULT_COLLATION
    String Value:
    Name:DUPLICATED
    String Value:N
    Name:SHARDED
    String Value:N
    Name:CREATED_APPID
    Number Value:
    Name:CREATED_VSNID
    Number Value:
    Name:MODIFIED_APPID
    Number Value:
    Name:MODIFIED_VSNID
    Number Value:
    End of Row Number # 1

    Name:OWNER
    String Value:SYS
    Name:OBJECT_NAME
    String Value:DUAL
    Name:SUBOBJECT_NAME
    String Value:
    Name:OBJECT_ID
    Number Value:143
    Name:DATA_OBJECT_ID
    Number Value:143
    Name:OBJECT_TYPE
    String Value:TABLE
    Name:CREATED
    Date Value:30-03-18
    Name:LAST_DDL_TIME
    Date Value:31-03-18
    Name:TIMESTAMP
    String Value:2018-03-30:21:37:22
    Name:STATUS
    String Value:VALID
    Name:TEMPORARY
    String Value:N
    Name:GENERATED
    String Value:N
    Name:SECONDARY
    String Value:N
    Name:NAMESPACE
    Number Value:1
    Name:EDITION_NAME
    String Value:
    Name:SHARING
    String Value:METADATA LINK
    Name:EDITIONABLE
    String Value:
    Name:ORACLE_MAINTAINED
    String Value:Y
    Name:APPLICATION
    String Value:N
    Name:DEFAULT_COLLATION
    String Value:USING_NLS_COMP
    Name:DUPLICATED
    String Value:N
    Name:SHARDED
    String Value:N
    Name:CREATED_APPID
    Number Value:
    Name:CREATED_VSNID
    Number Value:
    Name:MODIFIED_APPID
    Number Value:
    Name:MODIFIED_VSNID
    Number Value:
    End of Row Number # 2   
© www.soinside.com 2019 - 2024. All rights reserved.