Доступ к элементам типа записи Oracle PLSQL во время выполнения - PullRequest
1 голос
/ 01 мая 2019

Я использую динамический 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 стоит за этим, можем ли мы использовать это в кодировании?

Ответы [ 3 ]

0 голосов
/ 02 мая 2019

Моя первая рекомендация - использовать динамический 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 голосов
/ 08 мая 2019

@ Jon Спасибо за ваш вклад, это помогло.Также я могу перебирать столбцы без создания объектов, используя DBMS_SQL.DESCRIBE_COLUMNS.

** Ниже код все еще нуждается в тонкой настройке, но в основном работает:)

   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   
0 голосов
/ 01 мая 2019

Когда вы звоните bind_variable, вы привязываете фактическое значение к заполнителю. Поэтому, если вы предоставляете строку, которая является именем вашей переменной, то эта строка является значением, связанным с заполнителем.

Если массив содержит эти значения, тогда просто ссылайтесь на элемент массива, а не на имя этого элемента, как в:

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

Но я почти уверен, что это не то, что у тебя есть. Надеюсь, это поможет!

...