PLSQL получал значение свойства в записи динамически?
Во-первых, я действительно увлечен Oracle PLSQL, и я видел, как некоторые люди говорили, что это невозможно, а другие говорили, что это возможно, и я просто не могу этого добиться. Любая помощь будет принята с благодарностью.
Я пытаюсь прочитать значение столбца в типе записи динамически.
У меня есть сообщение с токенами, и мне нужно заменить токены значением из набора записей.
Таким образом, сообщение выглядит так: [status] by [agent_name]
У меня есть другое место, где я разбираю токены.
В Java-скрипте я знаю, что это может быть достигнуто с помощью: (будет работать в консоли)
var record = {
status : "Open",
agent_name : "John"
};
var record2 = {
status : "Close",
agent_name : "Joe"
};
var records = [record, record2];
var token1 = "status";
var token2 = "agent_name";
for( var i=0; i<records.length; i++){
console.log(records[i][token1] + " by " + records[i][token2]);
}
Results : Open by John
Close by Joe
Я хочу сделать то же самое в PLSQL
Вот мой тест PLSQL:
SET SERVEROUTPUT ON;
declare
TYPE my_record is RECORD
(
status VARCHAR2(30),
agent_name varchar2(30)
);
TYPE my_record_array IS VARRAY(6) OF my_record;
v_records my_record_array := my_record_array();
v_current_rec my_record;
v_current_rec2 my_record;
v_token varchar2(50):= 'agent_name';
v_token2 varchar2(50):= 'status';
begin
v_current_rec.status := 'Open';
v_current_rec.agent_name := 'John';
v_records.extend;
v_records(1) := v_current_rec;
v_current_rec2.status := 'Close';
v_current_rec2.agent_name := 'Ron';
v_records.extend;
v_records(2) := v_current_rec2;
FOR i IN 1..v_records.COUNT LOOP
--Hard coded
DBMS_OUTPUT.PUT_LINE(v_records(i).status || ' by ' || v_records(i).agent_name);
--Substitution vars entering v_records(i).status and v_records(i).agent_name for the prompts.
--How to do this without user interaction.
DBMS_OUTPUT.PUT_LINE(&status || ' by ' || &agent_name);
--Dynamically that doesn't work. How would this be accomplished
DBMS_OUTPUT.PUT_LINE(v_records(i).v_token || ' by ' || v_records(i).v_token2);
END LOOP;
END;
Я попытался использовать переменные подстановки, и это будет работать, если я использую:
DBMS_OUTPUT.PUT_LINE (& agent_name) и ввод v_records(i).agent_name при появлении запроса. Как мне сделать это на лету?
ОТВЕТ:
set serveroutput on;
DECLARE
type sr_record_map
IS
TABLE OF VARCHAR2(30) INDEX BY VARCHAR2(30);
type record_set
is
TABLE of sr_record_map index by BINARY_INTEGER;
v_current_rec sr_record_map;
v_record_set record_set;
v_token varchar2(30) := 'status';
v_token2 varchar2(30) := 'agent_name';
v_index number :=1;
begin
v_current_rec('status') := 'Open';
v_current_rec('agent_name') := 'John';
v_record_set(1) := v_current_rec;
v_current_rec('status') := 'Close';
v_current_rec('agent_name') := 'Joe';
v_record_set(2) := v_current_rec;
FOR i in 1..v_record_set.COUNT LOOP
v_current_rec := v_record_set(i);
DBMS_OUTPUT.PUT_LINE(v_current_rec(v_token) || ' by ' || v_current_rec(v_token2));
END LOOP;
end;
3 ответа
Использование ASSOCIATIVE ARRAY так же, как Maps в Java
DECLARE
type my_record_map
IS
TABLE OF VARCHAR2(30) INDEX BY VARCHAR2(30);
type my_record
IS
record
(
my_members my_record_map );
type final_map
IS
TABLE OF my_record INDEX BY VARCHAR2(20);
v_final_map final_map;
v_my_record_map my_record_map;
v_my_record my_record;
index_name VARCHAR2(100);
index_name_record VARCHAR2(100);
BEGIN
/* Individual Records as key value pairs with their Corresponding Columns */
/* You can put any member name inside */
v_my_record_map('status') := 'Open';
v_my_record_map('agent_name') := 'John';
v_my_record_map('added_by') := 'Maheshwaran';
/* Put it as a record */
v_my_record.my_members := v_my_record_map;
/* Put the record inside Another Map with any Key */
v_final_map('Record1') := v_my_record;
v_my_record_map('status') := 'Close';
v_my_record_map('agent_name') := 'Joe';
v_my_record_map('added_by') := 'Ravisankar';
v_my_record.my_members := v_my_record_map;
v_final_map('Record2') := v_my_record;
/* Take the First Key in the Outer most Map */
index_name := v_final_map.FIRST;
LOOP
/* status Here can be dynamic */
DBMS_OUTPUT.PUT_LINE(CHR(10)||'######'||v_final_map(index_name).my_members('status') ||' by '||v_final_map(index_name).my_members('agent_name')||'######'||CHR(10));
index_name_record := v_final_map(index_name).my_members.FIRST;
DBMS_OUTPUT.PUT_LINE('$ Ávailable Other Members + Values.. $'||CHR(10));
LOOP
DBMS_OUTPUT.PUT_LINE(' '||index_name_record ||'='||v_final_map(index_name).my_members(index_name_record));
index_name_record := v_final_map(index_name).my_members.NEXT(index_name_record);
EXIT WHEN index_name_record IS NULL;
END LOOP;
/* Next gives you the next key */
index_name := v_final_map.NEXT(index_name);
EXIT WHEN index_name IS NULL;
END LOOP;
END;
/
ВЫХОД:
######Open by John######
$ Ávailable Other Members + Values.. $
added_by=Maheshwaran
agent_name=John
status=Open
######Close by Joe######
$ Ávailable Other Members + Values.. $
added_by=Ravisankar
agent_name=Joe
status=Close
В качестве примера я прилагаю простой код, с помощью которого вы можете динамически сравнивать любые две записи любой таблицы по имени таблицы и значениям id, чтобы получить различия в данных.
DECLARE
p_id_1 NUMBER DEFAULT 697403;
p_id_2 NUMBER DEFAULT 697402;
p_table_name VARCHAR2(200) DEFAULT 'Name of the table';
V_result_1 VARCHAR2(2000);
V_result_2 VARCHAR2(2000);
CURSOR cur IS
SELECT * FROM ALL_TAB_COLUMNS WHERE table_name = p_table_name ;
BEGIN
FOR rec IN cur LOOP
EXECUTE IMMEDIATE
'SELECT ' || rec.COLUMN_NAME || ' FROM ' || P_TABLE_NAME ||
' WHERE ID = :1 '
INTO V_result_1
USING P_ID_1
;
EXECUTE IMMEDIATE
'SELECT ' || rec.COLUMN_NAME || ' FROM ' || P_TABLE_NAME ||
' WHERE ID = :1 '
INTO V_result_2
USING P_ID_2
;
IF NVL(v_result_1, 0) <> NVL(v_result_2,0) THEN
DBMS_OUTPUT.PUT_LINE('Column_name ' || rec.column_name || ' ' || v_result_1 || '\' || v_result_2);
END IF;
END LOOP;
END;
Я не думаю, что это может быть сделано с типом записи. Это может быть возможно с типом объекта, так как вы можете запрашивать поля из словаря данных, но обычно они нигде не доступны (хотя есть функция 11g, называемая PL/Scope, которая может разрешить ее, если она включена).
Поскольку вы определяете тип записи в том же месте, где вы его используете, и если у вас есть управляемое количество полей, может быть проще просто попытаться заменить каждый токен, который просто (!) Тратит немного ресурсов ЦП если его нет в сообщении:
declare
TYPE my_record is RECORD
(
status VARCHAR2(30),
agent_name varchar2(30)
);
TYPE my_record_array IS VARRAY(6) OF my_record;
v_records my_record_array := my_record_array();
v_current_rec my_record;
v_current_rec2 my_record;
v_message varchar2(50):= '[status] by [agent_name]';
v_result varchar2(50);
begin
v_current_rec.status := 'Open';
v_current_rec.agent_name := 'John';
v_records.extend;
v_records(1) := v_current_rec;
v_current_rec2.status := 'Close';
v_current_rec2.agent_name := 'Ron';
v_records.extend;
v_records(2) := v_current_rec2;
FOR i IN 1..v_records.COUNT LOOP
v_result := v_message;
v_result := replace(v_result, '[agent_name]', v_records(i).agent_name);
v_result := replace(v_result, '[status]', v_records(i).status);
DBMS_OUTPUT.PUT_LINE(v_result);
END LOOP;
END;
/
anonymous block completed
Open by John
Close by Ron
Это должно быть поддержано конечно; если поле добавлено к типу записи, чем соответствующее replace
нужно будет добавить к телу.
Я предполагаю, что в реальном мире текст сообщения с токенами будет передан процедуре. Я не уверен, что стоит разбирать токены, если только они вам не нужны для чего-то другого.