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;
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 have an Oracle (11g) SP which takes a comma separated string of inputs (e.g. 'Cats, Dogs, Monkeys'). The crux of the code is taken from Tony Andrews' blog and works as expected but it will not accept input strings greater than 4000 characters:
PROCEDURE delimstring_to_table ( p_delimstring IN VARCHAR2
, p_table OUT VARCHAR2_TABLE
, p_nfields OUT INTEGER
, p_delim IN VARCHAR2 DEFAULT ','
)
IS
v_string VARCHAR2(32767) := p_delimstring;
v_nfields PLS_INTEGER := 1;
v_table VARCHAR2_TABLE;
v_delimpos PLS_INTEGER := INSTR(p_delimstring, p_delim);
v_delimlen PLS_INTEGER := LENGTH(p_delim);
BEGIN
WHILE v_delimpos > 0
LOOP
v_table(v_nfields) := SUBSTR(v_string,1,v_delimpos-1);
v_string := SUBSTR(v_string,v_delimpos+v_delimlen);
v_nfields := v_nfields+1;
v_delimpos := INSTR(v_string, p_delim);
END LOOP;
v_table(v_nfields) := v_string;
p_table := v_table;
p_nfields := v_nfields;
END delimstring_to_table;
Reference - Tony Andrews Blog
The functions above are then used inside any relevant SPs within the package:
PROCEDURE Get_Animals(
p_Animals IN VARCHAR2 := NULL
, resultset_out OUT resultset_typ
, error_out OUT VARCHAR2
)
IS
BEGIN
IF p_Animals IS NULL THEN
OPEN resultset_out FOR
SELECT /*+ ALL_ROWS */ * FROM ANIMALS ORDER BY NAME;
ELSE
OPEN resultset_out FOR
SELECT * FROM ANIMALS where NAME in (SELECT * FROM TABLE (Comma_To_Table(p_RICs)));
END IF;
error_out := NULL;
EXCEPTION
WHEN OTHERS THEN
error_out := 'Get_Animals() -> ' || SUBSTR(SQLERRM,1,200);
END Get_Animals;
If the input string is > 4000 characters, I get the following error from the SP:
ORA-00604: error occurred at recursive SQL level 1
ORA-01003: no statement parsed - GET_ANIMALS
I have two questions:
Can I make the function work with input strings greater than 4000 characters?
Is there a more effective way of achieving the same result?
Any help or suggestions would be much appreciated.
Can I make the function work with input strings greater than 4000 characters?
Yes, you can use for example CLOB
Is there a more effective way of achieving the same result?
I saw in the comments of the blog a good answer, which is about a recursive solution.
just make some datatype changes for making it to work e.g.:
change the varchar2_table type to CLOB
TYPE varchar2_table IS TABLE OF CLOB INDEX BY BINARY_INTEGER;
change the VARCHAR2 datatype to CLOB in all p_delimstring occurences
change original SUBSTR functions to DBMS_LOB.SUBSTR
(if you need more info about that: http://docs.oracle.com/cd/A91202_01/901_doc/appdev.901/a89852/dbms_23b.htm)
CREATE OR REPLACE PACKAGE parse AS
/*
|| Package of utility procedures for parsing delimited or fixed position strings into tables
|| of individual values, and vice versa.
*/
TYPE varchar2_table IS TABLE OF CLOB INDEX BY BINARY_INTEGER;
PROCEDURE delimstring_to_table
( p_delimstring IN CLOB
, p_table OUT varchar2_table
, p_nfields OUT INTEGER
, p_delim IN VARCHAR2 DEFAULT ','
);
PROCEDURE table_to_delimstring
( p_table IN varchar2_table
, p_delimstring OUT CLOB
, p_delim IN VARCHAR2 DEFAULT ','
);
END parse;
/
CREATE OR REPLACE PACKAGE BODY parse AS
PROCEDURE delimstring_to_table
( p_delimstring IN CLOB
, p_table OUT varchar2_table
, p_nfields OUT INTEGER
, p_delim IN VARCHAR2 DEFAULT ','
)
IS
v_string CLOB := p_delimstring;
v_nfields PLS_INTEGER := 1;
v_table varchar2_table;
v_delimpos PLS_INTEGER := INSTR(p_delimstring, p_delim);
v_delimlen PLS_INTEGER := LENGTH(p_delim);
BEGIN
WHILE v_delimpos > 0
LOOP
v_table(v_nfields) := DBMS_LOB.SUBSTR(v_string,1,v_delimpos-1);
v_string := DBMS_LOB.SUBSTR(v_string,v_delimpos+v_delimlen);
v_nfields := v_nfields+1;
v_delimpos := INSTR(v_string, p_delim);
END LOOP;
v_table(v_nfields) := v_string;
p_table := v_table;
p_nfields := v_nfields;
END delimstring_to_table;
PROCEDURE table_to_delimstring
( p_table IN varchar2_table
, p_delimstring OUT CLOB
, p_delim IN VARCHAR2 DEFAULT ','
)
IS
v_nfields PLS_INTEGER := p_table.COUNT;
v_string CLOB;
BEGIN
FOR i IN 1..v_nfields
LOOP
v_string := v_string || p_table(i);
IF i != v_nfields THEN
v_string := v_string || p_delim;
END IF;
END LOOP;
p_delimstring := v_string;
END table_to_delimstring;
END parse;
/
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;
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;