Присвойте значение полю rowtype, где `field name` - строка
Я хочу присвоить значение полю типа строки, но я не знаю, как это сделать.
Предположим, что у меня есть таблица X внутри моей базы данных.
Предположим также, что у меня есть следующие переменные
a
(X%ROWTYPE
), представляющий строку таблицы Xb
(VARCHAR2
), содержащий имя столбца таблицы Xc
(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;