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 нужно будет добавить к телу.

Я предполагаю, что в реальном мире текст сообщения с токенами будет передан процедуре. Я не уверен, что стоит разбирать токены, если только они вам не нужны для чего-то другого.

Другие вопросы по тегам