First of all I am really rusty on Oracle PLSQL, and I have seen several folks say this cannot be done and others that say it can, and I am just not able to make it happen. Any help would be greatly appreciated.
I am trying to read the value of a column in a record type dynamically.
I have a message with tokens and I need to replace the tokens with value from the record set.
So the message looks like: [status] by [agent_name]
I have another place where I am parsing out the tokens.
In java script I know this can be accomplished with: (Will run in Console)
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
I want to do the same thing in PLSQL
Here is my test 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;
I tried using substitution variables, and that will work if I use:
DBMS_OUTPUT.PUT_LINE(&agent_name) and entering v_records(i).agent_name when prompted. How do I accomplish this on the fly?
ANSWER:
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;
Using an ASSOCIATIVE ARRAY just like Maps in 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;
/
OUTPUT:
######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
I don't think it can be done with a record type. It may be possible with an object type since you could query the fields from the data dictionary, but there isn't normally anywhere those are available (though there's an 11g facility called PL/Scope that might allow it if it's enabled).
Since you're defining the record type in the same place you're using it, and if you have a manageable number of fields, it might be simpler to just try to replace each token, which just (!) wastes a bit of CPU if one doesn't exist in the message:
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
It needs to be maintained of course; if a field is added to the record type than a matching replace will need to be added to the body.
I assume in the real world the message text, complete with tokens, will be passed to a procedure. I'm not sure it's worth parsing out the tokens though, unless you need them for something else.
As an example, I'm attaching simple code by which you can dynamically compare any two records of any table by name of table and id values, to get differences in data..
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;
Related
I'd like to create an in-memory array variable that can be used in my PL/SQL code. I can't find any collections in Oracle PL/SQL that uses pure memory, they all seem to be associated with tables. I'm looking to do something like this in my PL/SQL (C# syntax):
string[] arrayvalues = new string[3] {"Matt", "Joanne", "Robert"};
Edit:
Oracle: 9i
You can use VARRAY for a fixed-size array:
declare
type array_t is varray(3) of varchar2(10);
array array_t := array_t('Matt', 'Joanne', 'Robert');
begin
for i in 1..array.count loop
dbms_output.put_line(array(i));
end loop;
end;
Or TABLE for an unbounded array:
...
type array_t is table of varchar2(10);
...
The word "table" here has nothing to do with database tables, confusingly. Both methods create in-memory arrays.
With either of these you need to both initialise and extend the collection before adding elements:
declare
type array_t is varray(3) of varchar2(10);
array array_t := array_t(); -- Initialise it
begin
for i in 1..3 loop
array.extend(); -- Extend it
array(i) := 'x';
end loop;
end;
The first index is 1 not 0.
You could just declare a DBMS_SQL.VARCHAR2_TABLE to hold an in-memory variable length array indexed by a BINARY_INTEGER:
DECLARE
name_array dbms_sql.varchar2_table;
BEGIN
name_array(1) := 'Tim';
name_array(2) := 'Daisy';
name_array(3) := 'Mike';
name_array(4) := 'Marsha';
--
FOR i IN name_array.FIRST .. name_array.LAST
LOOP
-- Do something
END LOOP;
END;
You could use an associative array (used to be called PL/SQL tables) as they are an in-memory array.
DECLARE
TYPE employee_arraytype IS TABLE OF employee%ROWTYPE
INDEX BY PLS_INTEGER;
employee_array employee_arraytype;
BEGIN
SELECT *
BULK COLLECT INTO employee_array
FROM employee
WHERE department = 10;
--
FOR i IN employee_array.FIRST .. employee_array.LAST
LOOP
-- Do something
END LOOP;
END;
The associative array can hold any make up of record types.
Hope it helps,
Ollie.
You can also use an oracle defined collection
DECLARE
arrayvalues sys.odcivarchar2list;
BEGIN
arrayvalues := sys.odcivarchar2list('Matt','Joanne','Robert');
FOR x IN ( SELECT m.column_value m_value
FROM table(arrayvalues) m )
LOOP
dbms_output.put_line (x.m_value||' is a good pal');
END LOOP;
END;
I would use in-memory array. But with the .COUNT improvement suggested by uziberia:
DECLARE
TYPE t_people IS TABLE OF varchar2(10) INDEX BY PLS_INTEGER;
arrayvalues t_people;
BEGIN
SELECT *
BULK COLLECT INTO arrayvalues
FROM (select 'Matt' m_value from dual union all
select 'Joanne' from dual union all
select 'Robert' from dual
)
;
--
FOR i IN 1 .. arrayvalues.COUNT
LOOP
dbms_output.put_line(arrayvalues(i)||' is my friend');
END LOOP;
END;
Another solution would be to use a Hashmap like #Jchomel did here.
NB:
With Oracle 12c you can even query arrays directly now!
Another solution is to use an Oracle Collection as a Hashmap:
declare
-- create a type for your "Array" - it can be of any kind, record might be useful
type hash_map is table of varchar2(1000) index by varchar2(30);
my_hmap hash_map ;
-- i will be your iterator: it must be of the index's type
i varchar2(30);
begin
my_hmap('a') := 'apple';
my_hmap('b') := 'box';
my_hmap('c') := 'crow';
-- then how you use it:
dbms_output.put_line (my_hmap('c')) ;
-- or to loop on every element - it's a "collection"
i := my_hmap.FIRST;
while (i is not null) loop
dbms_output.put_line(my_hmap(i));
i := my_hmap.NEXT(i);
end loop;
end;
Sample programs as follows and provided on link also https://oracle-concepts-learning.blogspot.com/
plsql table or associated array.
DECLARE
TYPE salary IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
salary_list salary;
name VARCHAR2(20);
BEGIN
-- adding elements to the table
salary_list('Rajnish') := 62000; salary_list('Minakshi') := 75000;
salary_list('Martin') := 100000; salary_list('James') := 78000;
-- printing the table name := salary_list.FIRST; WHILE name IS NOT null
LOOP
dbms_output.put_line ('Salary of ' || name || ' is ' ||
TO_CHAR(salary_list(name)));
name := salary_list.NEXT(name);
END LOOP;
END;
/
Using varray is about the quickest way to duplicate the C# code that I have found without using a table.
Declare your public array type to be use in script
type t_array is varray(10) of varchar2(60);
This is the function you need to call - simply finds the values in the string passed in using a comma delimiter
function ConvertToArray(p_list IN VARCHAR2)
RETURN t_array
AS
myEmailArray t_array := t_array(); --init empty array
l_string varchar2(1000) := p_list || ','; - (list coming into function adding final comma)
l_comma_idx integer;
l_index integer := 1;
l_arr_idx integer := 1;
l_email varchar2(60);
BEGIN
LOOP
l_comma_idx := INSTR(l_string, ',', l_index);
EXIT WHEN l_comma_idx = 0;
l_email:= SUBSTR(l_string, l_index, l_comma_idx - l_index);
dbms_output.put_line(l_arr_idx || ' - ' || l_email);
myEmailArray.extend;
myEmailArray(l_arr_idx) := l_email;
l_index := l_comma_idx + 1;
l_arr_idx := l_arr_idx + 1;
END LOOP;
for i in 1..myEmailArray.count loop
dbms_output.put_line(myEmailArray(i));
end loop;
dbms_output.put_line('return count ' || myEmailArray.count);
RETURN myEmailArray;
--exception
--when others then
--do something
end ConvertToArray;
Finally Declare a local variable, call the function and loop through what is returned
l_array t_array;
l_Array := ConvertToArray('email1#gmail.com,email2#gmail.com,email3#gmail.com');
for idx in 1 .. l_array.count
loop
l_EmailTo := Trim(replace(l_arrayXX(idx),'"',''));
if nvl(l_EmailTo,'#') = '#' then
dbms_output.put_line('Empty: l_EmailTo:' || to_char(idx) || l_EmailTo);
else
dbms_output.put_line
( 'Email ' || to_char(idx) ||
' of array contains: ' ||
l_EmailTo
);
end if;
end loop;
I will pass some input parameters to stored procedure and do some calculation on tem. Finally I need to retrieve a few variables. I will not use any tables in database.
How to return only variables using stored procedure without any tables in Oracle?
Here is my code.
PROCEDURE SP_C3_PSTK_TAKING(
SCUR IN OUT CUST_CUR,
V_OUTLET IN VARCHAR2,
V_DATEOF_STOCK_TAKING IN DATE,
V_PSTK_VALUE IN NUMBER,
V_PSTK_VALUE_TP_CARDS IN NUMBER,
V_ACTUAL_STOCK_SHRINKAGE IN NUMBER,
V_NESCAFE_SHRINKAGE IN NUMBER,
V_ICE_CUBE_SHRINKAGE IN NUMBER,
V_TURN_OVER_FOR_THE_PERIOD IN NUMBER,
V_TP_CARD_SALES_FOR_THE_PERIOD IN NUMBER,
V_STOCK_TAKE_RECEIVED_DATE IN DATE,
V_STOCK_TAKE_DONE_BY IN VARCHAR2 ,
)
IS
TMPSTRSQL CLOB;
TMPSTRSQL1 CLOB;
V_ACTUAL_STOCK_SHRINKAGE_PER NUMBER;
V_TOTAL_SHORTAGE NUMBER;
V_T_OVER_EXCLUDING_TEL_CARDS NUMBER;
V_SHORTAGEPER NUMBER;
V_ALLOWANCE NUMBER;
V_VARIATION NUMBER
OUTLET VARCHAR(100);
DATEOF_STOCK_TAKING DATE;
PSTK_VALUE NUMBER;
PSTK_VALUE_TP_CARDS NUMBER;
ACTUAL_STOCK_SHRINKAGE NUMBER;
NESCAFE_SHRINKAGE NUMBER;
ICE_CUBE_SHRINKAGE NUMBER;
TURN_OVER_FOR_THE_PERIOD NUMBER;
TP_CARD_SALES_FOR_THE_PERIOD NUMBER;
STOCK_TAKE_RECEIVED_DATE DATE;
STOCK_TAKE_DONE_BY VARCHAR(100);
BEGIN
OUTLET := V_OUTLET ;
DATEOF_STOCK_TAKING := V_DATEOF_STOCK_TAKING ;
PSTK_VALUE := V_PSTK_VALUE ;
PSTK_VALUE_TP_CARDS := V_PSTK_VALUE_TP_CARDS ;
ACTUAL_STOCK_SHRINKAGE := V_ACTUAL_STOCK_SHRINKAGE ;
NESCAFE_SHRINKAGE := V_NESCAFE_SHRINKAGE ;
ICE_CUBE_SHRINKAGE := V_ICE_CUBE_SHRINKAGE ;
TURN_OVER_FOR_THE_PERIOD := V_TURN_OVER_FOR_THE_PERIOD ;
TP_CARD_SALES_FOR_THE_PERIOD := V_TP_CARD_SALES_FOR_THE_PERIOD ;
STOCK_TAKE_RECEIVED_DATE := V_STOCK_TAKE_RECEIVED_DATE ;
STOCK_TAKE_DONE_BY := V_STOCK_TAKE_DONE_BY ;
V_ACTUAL_STOCK_SHRINKAGE_PER := V_ACTUAL_STOCK_SHRINKAGE + ((V_ACTUAL_STOCK_SHRINKAGE/ 100) * 29 ) ;
V_TOTAL_SHORTAGE := V_ACTUAL_STOCK_SHRINKAGE_PER + V_NESCAFE_SHRINKAGE + V_ICE_CUBE_SHRINKAGE ;
V_T_OVER_EXCLUDING_TEL_CARDS := V_TURN_OVER_FOR_THE_PERIOD + V_TP_CARD_SALES_FOR_THE_PERIOD ;
V_SHORTAGEPER := V_TOTAL_SHORTAGE * 100 / V_T_OVER_EXCLUDING_TEL_CARDS ;
V_ALLOWANCE := (V_PSTK_VALUE /100 ) * 1 ;
TMPSTRSQL := ' ';
TMPSTRSQL1 := 'select V_ACTUAL_STOCK_SHRINKAGE_PER,V_TOTAL_SHORTAGE,V_T_OVER_EXCLUDING_TEL_CARDS,V_SHORTAGEPER,V_ALLOWANCE from dual';
OPEN SCUR FOR TMPSTRSQL1 || TMPSTRSQL;
END SP_C3_PSTK_TAKING;
Your problem lies in scope. In dynamic SQL we pass a string to a ref cursor the string must be runnable in the context of the database engine. In other words, could we run this query in SQL*Plus?
select V_ACTUAL_STOCK_SHRINKAGE_PER,
V_TOTAL_SHORTAGE,
V_T_OVER_EXCLUDING_TEL_CARDS,
V_SHORTAGEPER,
V_ALLOWANCE
from dual
Clearly not. DUAL has only one column, DUMMY. However we can select any number of literals from DUAL.
So what you need to do is turn that string into a projection of literals, like this:
TMPSTRSQL1 := 'select '
|| to_char(v_ACTUAL_STOCK_SHRINKAGE_PER) ||','
|| to_char(v_TOTAL_SHORTAGE) ||','
|| to_char(v_T_OVER_EXCLUDING_TEL_CARDS) ||','
|| to_char(v_SHORTAGEPER) ||','
|| to_char(v_ALLOWANCE) ||
' from dual';
OPEN SCUR FOR TMPSTRSQL1 || TMPSTRSQL;
END SP_C3_PSTK_TAKING;
I will pass some input parameters to stored procedure and I will do some calculation on it. Finally I need to retrieve few variables. Here I will not use any tables in database.
I'm trying to create a procedure that will allow me to write an existing row to another table dynamically but the row declaration and insert-statement in the following snippet don't work. The error message indicates that the view hasn't been identified although outputting the target_table.table_name works just fine.
More will be added to the block later on - such as a column with the operation (e.g. INSERT or UPDATE). This is just a simple example and the last procedure (pass_reference) is used to trigger the procedure.
Any help would be much appreciated.
CREATE OR REPLACE PROCEDURE denormalize (new_cursor sys_refcursor, target_table_name varchar)
IS
target_table user_tables%rowtype;
sql_target_table varchar(200) := 'select * from user_tables where table_name = :target_table_name';
row target_table%rowtype;
BEGIN
execute immediate sql_target_table into target_table using target_table_name;
LOOP
fetch new_cursor into row;
exit when new_cursor%notfound;
insert into target_table values row;
commit;
END LOOP;
END denormalize;
/
CREATE OR REPLACE PROCEDURE pass_reference
AS
new_cursor sys_refcursor;
BEGIN
open new_cursor for select * from sales where sales_id=1;
denormalize(new_cursor, 'NEW_SALES');
END;
/
please check this code, it's not working only as for example, as you see for working columns in your cursor should be named as columns in destination table.
I take this code from package that create html table in mail base on view, hope you found this example useful
good luck
declare
in_view_name varchar2(30);
in_table_name varchar2(30) := 'your_new_table';
out_rc number;
out_rc_txt varchar2(1000);
l_cursor number;
l_sql varchar2(50) := 'select * from ' || in_view_name;
l_col_cnt binary_integer;
l_col_tab dbms_sql.desc_tab;
l_column_value varchar2(4000);
l_is_empty boolean := true;
l_insert_header varchar2(1000);
l_insert varchar2(32000);
begin
out_rc := 0;
out_rc_txt := 'OK';
l_cursor := dbms_sql.open_cursor;
dbms_sql.parse(l_cursor, l_sql, dbms_sql.native);
l_col_cnt := dbms_sql.execute(l_cursor);
dbms_sql.describe_columns(l_cursor, l_col_cnt, l_col_tab);
l_insert_header := 'insert into '||in_table_name||'(';
if l_col_cnt > 0 then
-- header
for i in l_col_tab.first .. l_col_tab.last loop
dbms_lob.append(l_insert_header, l_col_tab(i).col_name);
if i != l_col_tab.last then
dbms_lob.append(l_insert_header, ',');
end if;
dbms_sql.define_column(l_cursor, i, l_column_value, 4000);
end loop;
l_insert_header := l_insert_header || ') values(';
-- data
while dbms_sql.fetch_rows(l_cursor) > 0 loop
l_is_empty := false;
l_insert := l_insert_header;
for i in l_col_tab.first .. l_col_tab.last loop
dbms_sql.column_value(l_cursor, i, l_column_value);
l_insert := l_insert || '''' || l_column_value || ''','
if not in_attachment then
dbms_lob.append(out_table, l_td);
end if;
if (not in_attachment) or (l_column_value is not null) then
dbms_lob.append(out_table, nvl(l_column_value, l_nbsp));
end if;
if (not in_attachment) or (i != l_col_tab.last) then
dbms_lob.append(out_table, l_tdc);
end if;
end loop;
l_insert := substr(l_insert, 1, length(l_insert) - 1) || ')';
execute immediate l_insert;
end loop;
end if;
dbms_sql.close_cursor(l_cursor);
end;
i working since some days with APEX and i added a Shuttle also a text field which contains after processing the selected values.
for the shuttle i have made a LOV (contains stations)
example:
Be
Fd
Au
...
the target is get all object's which contains the selected station's
but if i run the select i get more objects - also the object which does not contain any selected station, but i don’t know why
in the SQL statement i have the following: (ds_bess001 is the field for stations)
AND INSTR (:P4_BES_EN, zl.ds_bes001) > 0
for the shuttle i use PL/SQL below
DECLARE
v_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
v_in_text VARCHAR2 (500) := '';
BEGIN
v_selected := APEX_UTIL.STRING_TO_TABLE (:P4_BES);
FOR i IN 1 .. v_selected.COUNT
LOOP
v_in_text := v_in_text || v_selected (i);
IF i < v_selected.COUNT
THEN
-- v_in_text := v_in_text || ', ';
v_in_text := v_in_text || ',';
END IF;
END LOOP;
-- v_in_text := '''' || replace(:P1_SHUTTLE, ':', ''',''') || '''';
:P4_BES_EN := v_in_text;
END;
could anybody help me please?!
now i found the Problem i get the variable example Ap','Fd','Ab and the Problem is thats the variable miss the ' at the beginning and at the end
could anybody tel me how i could add the missing ' in the variable?
try it with
:P4_BES_EN := ''''||v_in_text||'''';
for ' at the beginning and the end.
and i think you have to alter your instr to something like this
:P4_BES_EN := ','''||v_in_text||''',';
AND INSTR (:P4_BES_EN, ','||zl.ds_bes001||',') > 0
e.g.:
DECLARE
v_in_text VARCHAR2 (500) := 'Ap'',''Fd'',''Ab';
BEGIN
v_in_text := ','''||v_in_text||''',';
dbms_output.put_line(v_in_text);
END;
Can you please help me how can I create two dimensional array in PL/SQL for Stored Procedure? The columns are dynamic so it can grow and change in types also.
Any help is appreciated. Thank you in advance!
I have the following code:
Type CAR_TABLE_ARRAY is varray(2) of varchar2(255);
TYPE CAR_TABLE_TYPE IS TABLE OF CAR_TABLE_ARRAY;
CAR_TABLE CAR_TABLE_TYPE;
CAR_TABLE := CAR_TABLE_TYPE();
CAR_TABLE.EXTEND(10);
CAR_TABLE(1)(1) := 'DODGE';
CAR_TABLE(2)(1) := 'FORD';
CAR_TABLE(3)(1) := 'MUSTANG';
CAR_TABLE(4)(1) := 'EDSEL';
CAR_TABLE(5)(1) := 'STUDEBAKER';
DBMS_OUTPUT.put_line( '1 ' || CAR_TABLE(1)(1) );
DBMS_OUTPUT.put_line( '2 ' || CAR_TABLE(2)(1) );
DBMS_OUTPUT.put_line( '3 ' || CAR_TABLE(3)(1) );
DBMS_OUTPUT.put_line( '4 ' || CAR_TABLE(4)(1) );
DBMS_OUTPUT.put_line( '5 ' || CAR_TABLE(5)(1) );
When I run I get the following error:
ORA-06531: Reference to uninitialized collection
Here's an example of using an multidimensional array in pl/sql. Here I use an array containing an array.
declare
type t_features is table of varchar(100) index by pls_integer;
type t_car_rec is record
(
make varchar2(50),
model varchar2(50),
features t_features
);
type t_car_tab is table of t_car_rec index by pls_integer;
car_tab t_car_tab;
procedure show_detail is
car_idx pls_integer;
features_idx pls_integer;
begin
car_idx := car_tab.first;
loop
exit when car_idx is null;
dbms_output.put_line('Details for ' || car_tab(car_idx).make || ' ' || car_tab(car_idx).model);
features_idx := car_tab(car_idx).features.first;
loop
exit when features_idx is null;
dbms_output.put_line(' =>' || car_tab(car_idx).features(features_idx));
features_idx := car_tab(car_idx).features.next(features_idx);
end loop;
car_idx := car_tab.next(car_idx);
end loop;
end;
begin
-- using sequential index values
car_tab(1).make := 'Ferrari';
car_tab(1).model := 'Testarossa';
car_tab(1).features(1) := 'Fast';
car_tab(1).features(2) := 'Looks cool';
car_tab(1).features(3) := 'Expensive';
-- using random index values (sparse)
car_tab(2).make := 'Acura';
car_tab(2).model := 'TSX';
car_tab(2).features(14) := 'Small';
car_tab(2).features(200) := 'Good MPG';
car_tab(2).features(36) := 'Inexpensive';
show_detail;
end;
Output would be:
Details for Ferrari Testarossa
=>Fast
=>Looks cool
=>Expensive
Details for Acura TSX
=>Small
=>Inexpensive
=>Good MPG
Hope that helps
VARRAY and nested table user-defined datatypes always have to be initialized using a constructor. You're doing that correctly for the nested table, but not for the VARRAYs that it contains. The simplest fix is to call the constructor in the assignment lines:
declare
Type CAR_TABLE_ARRAY is varray(2) of varchar2(255);
TYPE CAR_TABLE_TYPE IS TABLE OF CAR_TABLE_ARRAY;
CAR_TABLE CAR_TABLE_TYPE;
begin
CAR_TABLE := CAR_TABLE_TYPE();
CAR_TABLE.EXTEND(10);
CAR_TABLE(1) := CAR_TABLE_ARRAY('DODGE',null);
CAR_TABLE(2) := CAR_TABLE_ARRAY('FORD',null);
CAR_TABLE(3) := CAR_TABLE_ARRAY('MUSTANG',null);
CAR_TABLE(4) := CAR_TABLE_ARRAY('EDSEL',null);
CAR_TABLE(5) := CAR_TABLE_ARRAY('STUDEBAKER',null);
DBMS_OUTPUT.put_line( '1 ' || CAR_TABLE(1)(1) );
DBMS_OUTPUT.put_line( '2 ' || CAR_TABLE(2)(1) );
DBMS_OUTPUT.put_line( '3 ' || CAR_TABLE(3)(1) );
DBMS_OUTPUT.put_line( '4 ' || CAR_TABLE(4)(1) );
DBMS_OUTPUT.put_line( '5 ' || CAR_TABLE(5)(1) );
end;
That's because you're referencing the inner arrays which are not initialized.
Either add something like:
CAR_TABLE(1) := CAR_TABLE_ARRAY();
CAR_TABLE(1).EXTEND(1);
CAR_TABLE(2) := CAR_TABLE_ARRAY();
CAR_TABLE(2).EXTEND(1);
...
Or make the inner arrays (CAR_TABLE_ARRAY) as asociative arrays:
Type CAR_TABLE_ARRAY is TABLE of varchar2(255) index by binary_integer;