Create and populate Varray in Oracle SQL - sql

I'm trying to créate a Varray of beans type and populate it, but I'm in a hurry and don't find any usefull example.
arr=[[1,'A'],[2,'B'],[3,'C']]
This is my code:
create table my_table (NUM_OPERACIO NUMBER,TITULS varchar2(3)) ;
insert into my_table values(1,'A');
insert into my_table values(2,'B');
insert into my_table values(3,'C');
create TYPE item IS object( NUM_OPERACIO NUMBER, TITULS varchar2(3));
/
create TYPE arr IS VARRAY(10) OF item;
/
insert into arr values( select NUM_OPERACIO, TITULS from my_table);
FOR i IN 1..3 loop
dbms_output.put_line(arr (i));
END loop;
Help me achive this, please.
Thanks in advance

Oracle Setup:
create table my_table (NUM_OPERACIO NUMBER,TITULS varchar2(3)) ;
insert into my_table values(1,'A');
insert into my_table values(2,'B');
insert into my_table values(3,'C');
CREATE TYPE item IS object( NUM_OPERACIO NUMBER, TITULS varchar2(3));
/
CREATE TYPE item_array IS VARRAY(10) OF item;
/
PL/SQL:
DECLARE
arr item_array;
BEGIN
SELECT item( NUM_OPERACIO,TITULS )
BULK COLLECT INTO arr
FROM my_table;
FOR i IN 1..arr.COUNT loop
dbms_output.put_line(arr(i).NUM_OPERACIO || ', ' || arr(i).TITULS);
END loop;
END;
/

Related

How to execute SQL query stored in a table

I have a table having one of the columns that stores SQL query.
create table test1
(
name varchar(20),
stmt varchar(500)
);
insert into test1 (name, stmt)
values ('first', 'select id from data where id = 1;')
Data table is like:
create table data
(
id number,
subject varchar(500)
);
insert into data (id, subject) values (1, 'test subject1');
insert into data (id, subject) values (2, 'test subject2');
insert into data (id, subject) values (3, 'test subject2');
Now every time on insert in test1, I need to execute the query that gets inserted in stmt column of test1 and insert queried data to result table:
create table result
(
id number,
subject varchar(500)
);
For that I am writing a trigger that gets executed on every insert in test1 like as follows:
create or replace TRIGGER "TEST_AFTER_INSERT"
BEFORE INSERT or UPDATE ON test1
FOR EACH ROW
DECLARE
sql_stmt VARCHAR2(500);
BEGIN
select stmt into sql_stmt from data where name = :NEW.name;
insert into result(id, subject)
select id,subject from data where id in ('stmt');
END;
Could you please let me know how to achieve this, above trigger is throwing error that I am not able to understand.
You can use a dynamic query in your trigger as follows:
CREATE OR REPLACE TRIGGER "TEST_AFTER_INSERT" AFTER -- CHANGED IT TO AFTER AS NAME SUGGESTS
INSERT OR UPDATE ON TEST1
FOR EACH ROW -- REMOVED DECLARE SECTION
BEGIN
EXECUTE IMMEDIATE 'INSERT INTO result
SELECT ID, SUBJECT FROM DATA WHERE ID IN ('
|| RTRIM(:NEW.STMT, ';')
|| ')';
-- SINGLE QUERY TO INSERT THE DATA
-- USED RTRIM AS STMT HAS ; AT THE END
END;
Cheers!!
Consider direct insertion :
CREATE OR REPLACE TRIGGER "TEST_AFTER_INSERT"
BEFORE INSERT or UPDATE ON test1
FOR EACH ROW
DECLARE
BEGIN
insert into result(id, subject)
select id, subject from data where name = :NEW.name;
END;

Using SQL object type in procedure and use them in pipeline function

Here i am trying to use a Pipeline function which will take a Collection as input and return a collection after some validation which i use to insert data into a table;
Here are some test objects which i have created to explain my problem.
create table tst_cri_sdb (icri number, datesitu date, curr varchar2(3), ctmstm varchar2(10));
insert into TST_CRI_SDB values (100, to_date('13032019','ddmmyyyy'), 'EUR', 'STM');
insert into TST_CRI_SDB values (101, to_date('14032019','ddmmyyyy'), 'GBP', 'CTM');
insert into TST_CRI_SDB values (102, to_date('15032019','ddmmyyyy'), 'USD', 'STM');
insert into TST_CRI_SDB values (103, to_date('16032019','ddmmyyyy'), 'INR', 'CTM');
insert into TST_CRI_SDB values (104, to_date('17032019','ddmmyyyy'), 'EUR', 'STM');
create type tst_rec as object (icri number, datesitu date, curr varchar2(3), ctmstm varchar2(10));
create type tst_table_rec as table of tst_rec;
create table sdb_gpcs (curr varchar2(3), ctmstm varchar2(5), goca number, cust_grp varchar2(30));
insert into sdb_gpcs values ('EUR','CTM', 100345 ,'A1105');
insert into sdb_gpcs values ('EUR','CTM', 200345 ,'A4405');
insert into sdb_gpcs values ('EUR','STM', 300345 ,'A3305');
insert into sdb_gpcs values ('USD','CTM', 500345 ,'A5505');
insert into sdb_gpcs values ('USD','STM', 600345 ,'A6605');
insert into sdb_gpcs values ('USD','STM', 700345 ,'A7705');
select * from sdb_gpcs where curr = 'EUR' and ctmstm = 'CTM';
create table tst_cri_plus_sdb (deal_id number, datesitu date, acc_code number, acca_cust_grp varchar2(10), curr varchar2(3), ctmstm varchar2(5));
create type tst_plus_rec as object(deal_id number, datesitu date, acc_code number, acca_cust_grp varchar2(10), curr varchar2(3), ctmstm varchar2(5));
create type tst_plus_table_rec as table of tst_plus_rec;
create or replace function get_plus_sdb_w ( p_tab IN tst_table_rec)
return tst_plus_table_rec PIPELINED
is
l_rec tst_plus_rec;
begin
for i in 1..p_tab.count
loop
for j in (select * from sdb_gpcs)
loop
l_rec := tst_plus_rec(p_tab(i).icri, p_tab(i).datesitu, j.goca, j.cust_grp ,p_tab(i).curr, p_tab(i).ctmstm);
PIPE row(l_rec);
end loop;
end loop;
end;
CREATE or replace procedure tst_insert
is
cursor c1 is select * from tst_cri_sdb;
l_tab tst_table_rec := tst_table_rec();
l_tab_plus tst_plus_table_rec := tst_plus_table_rec();
begin
for i in c1j
loop
l_tab.extend;
l_tab(l_tab.last) := tst_table_rec(tst_rec(i.icri, i.datesitu, i.curr,
i.ctmstm));
end loop;
SELECT *
bulk collect into l_tab_plus
FROM TABLE(get_plus_sdb_w(l_tab));
forall idx IN INDICES OF l_tab_plus
insert into tst_cri_plus_sdb values l_tab(idx);
end;
My idea here is to collect all data tst_cri_sdb table into a collection then pass this collection to a pipeline function which will return a collection again so that i can bulk collect it and insert it in the table tst_cri_plus_sdb.
Help me to collect data in the procedure in a collection and also with pipeline function.
Please ask me more information if required.
I am using --
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
Your procedure has a couple of simple errors; the cursor name is c1 but you later refer to it as c1j, and you're trying to assign a whole table object instance as an element of a table, instead of just a record:
l_tab(l_tab.last) := tst_rec(i.icri, i.datesitu, i.curr, i.ctmstm);
The bigger issue is that you're mixing object and native types. When you unnest the collection with the table() clause you get multiple columns back, not a single object type; so you would have to reconstruct your object:
select tst_plus_rec(deal_id, datesitu, acc_code, acca_cust_grp, curr, ctmstm)
bulk collect into l_tab_plus
from table(get_plus_sdb_w(l_tab));
and then the forall insert would have to return to each object attribute:
forall idx IN INDICES OF l_tab_plus
insert into tst_cri_plus_sdb (deal_id, datesitu, acc_code, acca_cust_grp, curr, ctmstm)
values (l_tab_plus(idx).deal_id, l_tab_plus(idx).datesitu, l_tab_plus(idx).acc_code,
l_tab_plus(idx).acca_cust_grp, l_tab_plus(idx).curr, l_tab_plus(idx).ctmstm);
So putting those together:
create or replace procedure tst_insert
is
cursor c1 is select * from tst_cri_sdb;
l_tab tst_table_rec := tst_table_rec();
-- no need to initialise this one as bulk collect will replace it
-- l_tab_plus tst_plus_table_rec := tst_plus_table_rec();
l_tab_plus tst_plus_table_rec;
begin
for i in c1
loop
l_tab.extend;
l_tab(l_tab.last) := tst_rec(i.icri, i.datesitu, i.curr, i.ctmstm);
end loop;
select tst_plus_rec(deal_id, datesitu, acc_code, acca_cust_grp, curr, ctmstm)
bulk collect into l_tab_plus
from table(get_plus_sdb_w(l_tab));
forall idx IN INDICES OF l_tab_plus
insert into tst_cri_plus_sdb (deal_id, datesitu, acc_code, acca_cust_grp, curr, ctmstm)
values (l_tab_plus(idx).deal_id, l_tab_plus(idx).datesitu, l_tab_plus(idx).acc_code,
l_tab_plus(idx).acca_cust_grp, l_tab_plus(idx).curr, l_tab_plus(idx).ctmstm);
end;
/
exec tst_insert;
select * from tst_cri_plus_sdb;
DEAL_ID DATESITU ACC_CODE ACCA_CUST_ CUR CTMST
---------- ---------- ---------- ---------- --- -----
100 2019-03-13 100345 A1105 EUR STM
100 2019-03-13 200345 A4405 EUR STM
100 2019-03-13 300345 A3305 EUR STM
100 2019-03-13 500345 A5505 EUR STM
100 2019-03-13 600345 A6605 EUR STM
100 2019-03-13 700345 A7705 EUR STM
101 2019-03-14 100345 A1105 GBP CTM
101 2019-03-14 200345 A4405 GBP CTM
...
104 2019-03-17 600345 A6605 EUR STM
104 2019-03-17 700345 A7705 EUR STM
30 rows selected.
You don't need the intermediate collection with bulk collect and forall though, you can insert directly:
create or replace procedure tst_insert
is
cursor c1 is select * from tst_cri_sdb;
l_tab tst_table_rec := tst_table_rec();
-- you don't need this variable at all now
-- l_tab_plus tst_plus_table_rec;
begin
for i in c1
loop
l_tab.extend;
l_tab(l_tab.last) := tst_rec(i.icri, i.datesitu, i.curr, i.ctmstm);
end loop;
insert into tst_cri_plus_sdb
select *
from table(get_plus_sdb_w(l_tab));
end;
/
which gets the same result. And you could avoid the populating-loop by changing that to a bulk-collect:
create or replace procedure tst_insert
is
l_tab tst_table_rec;
begin
select tst_rec(icri, datesitu, curr, ctmstm)
bulk collect into l_tab
from tst_cri_sdb;
insert into tst_cri_plus_sdb
select *
from table(get_plus_sdb_w(l_tab));
end;
/
db<>fiddle
In later versions of Oracle you could do this all in a package without any schema-level object types; but as you're on 11g that won't work (the table() clause will throw PLS-00642).

Is there any possibility of creating view or table based on the output of a dynamically opened ref cursor?

I have learnt how to return a dynamically opened ref cursor. Now based on the output, I want to create a table or a view.
Input table:
create table sales
(s_sale_name varchar2(20),
s_date_sal date,
s_qty number(10)
);
Records
insert into sales values ('Norb','10-MAR-2019',10000);
insert into sales values ('Bert','10-MAR-2019',5000);
insert into sales values ('Alba','10-MAR-2019',4000);
insert into sales values ('Rob','10-MAR-2019',200000);
insert into sales values ('Norb','11-MAR-2019',5000);
insert into sales values ('Bert','11-MAR-2019',13000);
insert into sales values ('Rob','11-MAR-2019',80000);
insert into sales values ('Norb','12-MAR-2019',1000);
insert into sales values ('Bert','12-MAR-2019',4000);
insert into sales values ('Rob','12-MAR-2019',40000);
insert into sales values ('Alba','12-MAR-2019',2000);
Query output
sales_name 10-MAR-2019 11-MAR-2019 12-MAR-2019
Norb 10000 5000 1000
Bert 5000 13000 4000
Alba 4000 0 2000
Rob 200000 80000 40000
Now the result should be saved in a table or a view. I have learnt how to return a dynamically opened ref cursor so far.
------Here is the procedure I used -----------
create or replace package p_sales_pkg
as
type rc is ref cursor;
procedure get_query( p_cursor in out rc, p_start date, p_end date );
end;
/
create or replace package body p_sales_pkg
as
procedure get_query( p_cursor in out rc, p_start date, p_end date )
is
l_query long := 'select s_name ';
begin
for i in 1 .. trunc(p_end)-trunc(p_start)+1
loop
l_query := l_query || ', sum( decode( trunc(s_date), ' ||
'to_date( ''' || to_char(p_start+i-1,'yyyymmdd') ||
''', ''yyyymmdd'' ), s_qty, 0 )) "' ||
to_char(p_start+i-1) || '"';
end loop;
l_query := l_query || ' from sales group by s_name';
open p_cursor for l_query;
end;
end;
/
set autoprint on
var x refcursor
exec nw_demo_pkg.get_query( :x, '10-MAR-19', '13-MAR-19' );
This is really a very nice and challenging question. I disagree on the #APC point on SELECT part of a CREATE TABLE ... AS SELECT statement. Well we definitely can't do that. What i believe is to every problem in Oracle, there exists a solution.
You requirement can be achieved using a NESTED TABLE. See below:
Set Up:
create table sales
(s_sale_name varchar2(20),
s_date_sal date,
s_qty number(10)
);
/
insert into sales values ('Norb','10-MAR-2019',10000);
insert into sales values ('Bert','10-MAR-2019',5000);
insert into sales values ('Alba','10-MAR-2019',4000);
insert into sales values ('Rob','10-MAR-2019',200000);
insert into sales values ('Norb','11-MAR-2019',5000);
insert into sales values ('Bert','11-MAR-2019',13000);
insert into sales values ('Rob','11-MAR-2019',80000);
insert into sales values ('Norb','12-MAR-2019',1000);
insert into sales values ('Bert','12-MAR-2019',4000);
insert into sales values ('Rob','12-MAR-2019',40000);
insert into sales values ('Alba','12-MAR-2019',2000);
---Created an Object of Sales table to hold intermediate result
create or replace type sales_obj is OBJECT
(obj_sale_name varchar2(20),
obj_date_sal date,
obj_qty number(10)
);
/
-- Table of Sales Object.
create or replace type vtest1Tab is table of sales_obj;
/
Anonymous Block to Create table ccc:
DECLARE
VAR VTEST1TAB ;
vsql varchar2(500);
BEGIN
vsql := 'create table ccc(col1) NESTED TABLE COL1 STORE AS TAB1
as
Select cast(multiset(Select * from SALES) as VTEST1TAB )
from dual
';
Execute immediate vsql ;
END;
Output:
SQL> Select p.*
from ccc c,
table(c.COL1) p ;
In this link , The reply by "Zlatko Sirotic" covers exactly how to identify columns of the cursor and print them.
Look for "dyn_fetch", as the package is generic enough, it can work with any query for printing data. You can use the same approach to insert the data into a table that is created dynamically.

How to Create Stored procedure in DB2

Can you please help me to create a below Oracle procedure in DB2? Same table name with columns are available in DB2 also but below script is not working
CREATE OR REPLACE PROCEDURE sample_proc (ACCT_NO in CHAR,p_cursor out SYS_REFCURSOR)
is
BEGIN
OPEN p_cursor FOR
select sampl1,sample2,sample3
from
table_test b
where
rec_id='A'
and sample3=ACCT_NO ;
END;
If you want a return better use function here are some example to get collection
CREATE TABLE table_test
(
sample1 VARCHAR2 (1000),
sample2 VARCHAR2 (1000),
sample3 VARCHAR2 (1000)
);
insert into table_test ( sample1,sample2 ,sample3)
values ('daftest1','dsdtest1','sstsest3');
insert into table_test ( sample1,sample2 ,sample3)
values ('FAStest1','fstest1','sstsest3');
insert into table_test ( sample1,sample2 ,sample3)
values ('sdtest1','asdtest1','fstest3');
insert into table_test ( sample1,sample2 ,sample3)
values ('test2','test2','test123');
CREATE OR REPLACE TYPE TEST_REC
AS OBJECT (
sample1 VARCHAR2(1000)
,sample2 VARCHAR2(1000)
,sample3 VARCHAR2(1000)
);
CREATE OR REPLACE TYPE TEST_REPORT_TABLE
AS TABLE OF TEST_REC;
CREATE OR REPLACE FUNCTION testing (p_acct_no IN varchar2)
RETURN test_report_table
IS
v_rec test_rec;
v_test_report_table test_report_table := test_report_table();
BEGIN
FOR i IN (SELECT sample1,sample2,sample3
FROM table_test b
--where rec_id='A'
where sample3=p_acct_no)
LOOP
v_rec:=test_rec(NULL,NULL,NULL);
dbms_output.put_line(i.sample1);
v_rec.sample1:=i.sample1;
v_rec.sample2:=i.sample2;
v_rec.sample3:=i.sample3;
v_test_report_table.EXTEND;
v_test_report_table(v_test_report_table.COUNT) :=v_rec;
END LOOP;
RETURN v_test_report_table;
END;
select * from table(testing(p_acct_no=>'sstsest3'))
Or better use bulk collect if you don't need to add rows dynamically
CREATE OR REPLACE FUNCTION JSTRAUTI.testing (p_acct_no IN varchar2)
RETURN test_report_table
IS
v_test_report_table test_report_table := test_report_table();
BEGIN
SELECT test_rec(sample1,sample2,sample3)
bulk collect into v_test_report_table
FROM table_test b
--where rec_id='A'
where sample3=p_acct_no;
RETURN v_test_report_table;
END;
result the same.

select forall array in sql statement(PL/SQL)

Type tabArray IS TABLE OF TABLE%ROWTYPE;
tableArray tabArray ;
--fill array
SELECT *
BULK COLLECT INTO tableArray
FROM TABLE
WHERE TABLE.field = ....
--work
FOR pos IN 1..tableArray .count
LOOP
dbms_output.put_line(pos||' '||audArray(pos).field);
end loop;
--doesn't work
SELECT * TABLE2
WHERE TABLE2.field in (SELECT filed FROM FORALL tableArray );
Main question: how can I use my array in sql statement (in) ?
First you have to create a type in SQL then can use as given below
CREATE TYPE FRUIT_TT AS TABLE OF VARCHAR2(100)
SELECT column_value AS val
FROM TABLE(FRUIT_TT('Apple','Banana','Apricot'))
WHERE column_value NOT LIKE 'A%';
Here a type FRUIT_TT is created and using it in SQL query.
Here is an example, you just need to adjust your SQL statement.
CREATE TYPE col_ntt IS TABLE OF NUMBER;
CREATE TABLE num_tab
(
col NUMBER
);
INSERT INTO num_tab VALUES(1);
INSERT INTO num_tab VALUES(2);
INSERT INTO num_tab VALUES(4);
DECLARE
l_col1 col_ntt := col_ntt(1, 2, 3);
l_col2 col_ntt;
BEGIN
SELECT *
BULK COLLECT INTO l_col2
FROM num_tab
WHERE col IN (SELECT column_value FROM TABLE(l_col1));
FOR indx IN 1..l_col2.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(l_col2(indx));
END LOOP;
END;
/*
1
2
*/