Доступ к полям записи по их имени в Oracle - PullRequest
0 голосов
/ 09 мая 2020

Я хочу знать, существует ли что-то вроде Reflection API в PL / SQL или нет.

У меня есть таблица типа

create table my_table (
  id number,
  value1 number,
  value2 number, 
  value3 number,
  value4 number,
  value5 number);

И у меня есть переменная как

rec as my_table%rowtype
... fill rec

insert into my_table values rec;

есть ли способ динамически заполнить поле rec по его имени.

Я имею в виду, что знаю индекс (в данном случае что-то от 1 до 5), поэтому я хочу чтобы установить 'value'||index на что-то.

Как и в моем реальном случае, последний индекс намного больше 5, использование набора if/elsif неуместно. Между прочим, количество полей увеличивается в долгосрочной перспективе (например, value6, value7 могут быть добавлены в следующем году и так далее, поэтому я хочу написать какой-то код, который не будет изменяться в каждом новом столбце).

1 Ответ

0 голосов
/ 19 мая 2020

Вы можете получить доступ к переменным в программе с помощью Dynami c SQL, только если они доступны глобально. Если вы объявите, что записываете в spe c, вы можете создавать служебные функции, которые будут использовать EXECUTE IMMEDIATE для создания небольшого блока PL / SQL для установки значения. Вот простой пример того, что вы ищете. Обратите внимание, что вы можете перегрузить установленную процедуру, чтобы ваши типы данных остались нетронутыми.

CREATE TABLE my_table (
  value1 NUMBER,
  value2 VARCHAR2(100), 
  value3 DATE);

CREATE OR REPLACE PACKAGE pkg_my_table IS
  my_table_rec my_table%ROWTYPE;

  FUNCTION build_statement(i_record IN VARCHAR2,
                           i_field  IN VARCHAR2) RETURN VARCHAR2;

  PROCEDURE set_value(i_record IN VARCHAR2,
                      i_field  IN VARCHAR2,
                      i_value  IN VARCHAR2);

  PROCEDURE set_value(i_record IN VARCHAR2,
                      i_field  IN VARCHAR2,
                      i_value  IN NUMBER);

  PROCEDURE set_value(i_record IN VARCHAR2,
                      i_field  IN VARCHAR2,
                      i_value  IN DATE);

  PROCEDURE insert_a_row;
END pkg_my_table;
/

CREATE OR REPLACE PACKAGE BODY pkg_my_table IS
  FUNCTION build_statement(i_record IN VARCHAR2,
                           i_field  IN VARCHAR2) RETURN VARCHAR2 IS
  BEGIN
    RETURN 'BEGIN ' || lower($$PLSQL_UNIT) || '.' || i_record || '.' || i_field || ' := :x; END;';
  END build_statement;

  PROCEDURE set_value(i_record IN VARCHAR2,
                      i_field  IN VARCHAR2,
                      i_value  IN VARCHAR2) IS
  BEGIN
    EXECUTE IMMEDIATE build_statement(i_record => i_record,
                                      i_field  => i_field)
      USING i_value;
  END set_value;

  PROCEDURE set_value(i_record IN VARCHAR2,
                      i_field  IN VARCHAR2,
                      i_value  IN NUMBER) IS
  BEGIN
    EXECUTE IMMEDIATE build_statement(i_record => i_record,
                                      i_field  => i_field)
      USING i_value;
  END set_value;

  PROCEDURE set_value(i_record IN VARCHAR2,
                      i_field  IN VARCHAR2,
                      i_value  IN DATE) IS
  BEGIN
    EXECUTE IMMEDIATE build_statement(i_record => i_record,
                                      i_field  => i_field)
      USING i_value;
  END set_value;

  PROCEDURE insert_a_row IS
  BEGIN
    my_table_rec := NULL;
    set_value(i_record => 'my_table_rec',
              i_field  => 'value1',
              i_value  => 42);
    set_value(i_record => 'my_table_rec',
              i_field  => 'value2',
              i_value  => 'forty-two');
    set_value(i_record => 'my_table_rec',
              i_field  => 'value3',
              i_value  => to_date('1/1/1942',
                                  'mm/dd/yyyy'));
    INSERT INTO my_table
    VALUES my_table_rec;
  END insert_a_row;
END pkg_my_table;
/

BEGIN
  pkg_my_table.insert_a_row;
END;
/

SELECT *
  FROM my_table;
-- 42   forty-two   1/1/1942

Остерегайтесь глобальных переменных: вам нужно правильно их сбросить, прежде чем использовать их снова, иначе вы можете перенести данные из предыдущих вызовов. Установка для всей переменной типа записи значения NULL приведет к сбросу всех базовых полей (удобно).

Вы также будете подвержены ошибкам ORA-04068: существующее состояние пакетов было отброшено, если вы скомпилируете PL / SQL с глобальными объектами, где есть активные сеансы, ссылающиеся на ваш код. Обычно это не проблема, но это разница в поведении.

...