Присвойте значение полю rowtype, где `field name` - строка

Я хочу присвоить значение полю типа строки, но я не знаю, как это сделать.

Предположим, что у меня есть таблица X внутри моей базы данных.

Предположим также, что у меня есть следующие переменные

  • a (X%ROWTYPE), представляющий строку таблицы X
  • b (VARCHAR2), содержащий имя столбца таблицы X
  • c (VARCHAR2), содержащий то, что я хочу хранить внутри AB

Что я хочу сделать: что-то вроде a.b := c,

Я придумал что-то вроде этого:

EXECUTE IMMEDIATE 'SELECT '|| c || ' INTO a.' || b || ' FROM DUAL';

Видимо, это не правильный путь. Я получаю ORA-0095: ошибка отсутствующего ключевого слова.

Кто-нибудь может мне с этим помочь?

Вот полный код:

DECLARE
    tRow            MyTable%ROWTYPE;
    col_name        VARCHAR(10) := 'Length';
    nValue          NUMBER(12,4) := 0.001;
    dynamic_request VARCHAR(300);
BEGIN 
    dynamic_request := 'SELECT '|| nValue || ' INTO tRow.' || col_name || ' FROM DUAL';
    EXECUTE IMMEDIATE dynamic_request;
END;

3 ответа

Решение

Хорошо, я решил это!

Краткий ответ: использование глобальной переменной делает свое дело

Ответ Разработка

Давайте рассмотрим два факта о динамических блоках PL/SQL (то есть блоках PL/SQL, записанных в виде строк, которые должны быть выполнены через EXECUTE IMMEDIATE заявление)

[1] При создании динамического блока PLSQL не существует такой вещи, как переменная область видимости. Что я имею в виду, если вы делаете что-то вроде этого:

CREATE OR REPLACE PROCEDURE DynamicVariableAssignment(
   theString IN VARCHAR2
 ) 
IS
BEGIN 
   EXECUTE IMMEDIATE 'BEGIN theString := ''test''; END; ';
END;

это просто не будет работать, потому что сфера theString не переносится в динамический блок PL/SQL. Другими словами, динамический блок PL/SQL не "наследует" ни одну переменную, где бы он ни выполнялся.

[2] Вы можете сказать "ОК, без паники, я могу дать аргументы ввода / вывода для моего динамического блока PL/SQL, верно?". Конечно, вы можете, но угадайте, что: вы можете давать только типы SQL как in/out! Истинные типы PL/SQL с другой стороны, такие как myTable%rowtype, не принимаются в качестве входных данных для динамического блока PL/SQL. Так что ответ hmmftg тоже не сработает:

-- I've reduced the code to the interesting part
dynamic_request := 'BEGIN :t_row.' || col_name || ':= 0.001; END;';
EXECUTE IMMEDIATE dynamic_request USING IN OUT tRow;
-- (where tRow is of type myTable%ROWTYPE)

поскольку tRow имеет тип MyTable%ROWTYPE, он не является допустимым типом SQL и поэтому недопустим в качестве входных данных для динамического блока PL/SQL.

Решение Кто бы мог подумать, что глобальные переменные придут и спасут день? Как мы уже говорили в [1], у нас нет ссылки на какую-либо переменную вне динамического блока PL/SQL. НО мы все еще можем получить доступ к глобальным переменным, определенным в заголовках пакетов!

Давайте предположим, что у меня есть пакет kingPackage в котором я определяю следующее:

tempVariable  myTable%ROWTYPE;

Тогда я могу сделать это:

ЗАКЛЮЧИТЕЛЬНЫЙ КОД (только тело)

-- Copy tRow into temp variable
kingPackage.tempVariable := tRow;

-- We modify the column of the temp variable
vString := 'BEGIN kingPackage.tempVariable.' || col_val || ' := ' || TO_CHAR(vNumber) ||'; END;'; 
EXECUTE IMMEDIATE vString;    

-- The column value has been updated \o/ 
tRow := kingPackage.tempVariable;

Ну вот, ребята! Хорошего дня

Попробуй это:

CREATE OR REPLACE PROCEDURE ROW_CHANGER(
    tRow            IN MyTable%ROWTYPE,
    col_name        IN VARCHAR,
    nValue          IN NUMBER) 
   AS
    dynamic_request VARCHAR(300);
BEGIN 
    dynamic_request := 'BEGIN  :t_row.'||COL_NAME ||':= :n_value; END;';
    EXECUTE IMMEDIATE dynamic_request
         USING IN OUT  TROW, IN nValue;
END;

это потому что в вашем EXECUTE IMMEDIATE tRow MyTable%ROWTYPE не определено,

поэтому мы определили это с using заявление.

Я нашел вариант, который вообще не требует использования пакета и может быть выполнен из анонимного блока. Вам просто нужно использовать временную переменную для доступа к полям входящей записи, а затем снова назначить временную переменную параметру. По крайней мере, это работает на 19c, и все исходные значения переносятся.

Используя ваш оригинальный пример...

      DECLARE
   v_row  MyTable%ROWTYPE;
   v_col  VARCHAR(10) := 'column2';
   v_val  VARCHAR(1)  := 'B';
   v_sql  VARCHAR(300);
BEGIN
   v_row.column1 := 'A';
   v_sql := 'DECLARE tmp MyTable%ROWTYPE := :0; BEGIN tmp.' || v_col || ' := :1; :0 := tmp; END;';
   EXECUTE IMMEDIATE v_sql using in out v_row, in v_val;

   dbms_output.put_line(v_row.column1); -- A
   dbms_output.put_line(v_row.column2); -- B
END;
Другие вопросы по тегам