Not able to use dynamic SQL in bulk collect and forall - plsql

Hi friends am writing a stored procedure that selects and inserts data from one table to another table in the same database but different users, to speed up the insert am trying to use bulk collect and forall but the problem is am using dynamic SQL in the procedure that picks table name, it's corresponding primary key name and the date validation columns from another status table
For eg: consider the following query
Select empid from emp_tab where trunc(insert_time)=trunc(sysdate)
Here the PRIMARY KEY - empid, TABLENAME- emp_tab, date validation column - insert_time will be dynamically picked by the procedure during run time of the procedure from another status table but am not able incorporate the bulk collect and forall into dynamic SQL for bulk collect am getting
inconsistent data type
error and forall am getting
virtual column
error
Does bulk and forall supports dynamic sql????
If not then is there any other way to speed up the insert like forall
Any suggestions are welcome
Thanks in advance

Yes, they do. Despite BULK COLLECT being a powerful tool, it can be tricky with all its limitation. Here is a example using REF CURSOR, but you can build the TYPES according to your necessity.
-- create source data
CREATE TABLE temp_table_source AS
(SELECT ROWNUM id,
dbms_random.String('U', 10) name,
Round(dbms_random.Value(1, 7000)) salary
FROM dual
CONNECT BY LEVEL <= 5000 -- number of rows for the test
);
-- create destination table (empty)
CREATE TABLE temp_table_dest AS
SELECT * FROM temp_table_source WHERE 1 = 2;
-- Dynamic bulk anonymous block
DECLARE
v_stmt VARCHAR2(100);
v_insert VARCHAR2(100);
TYPE rec_source IS RECORD ( -- a type to represent your data
id temp_table_source.id%TYPE,
name temp_table_source.name%TYPE,
salary temp_table_source.salary%TYPE
);
TYPE t_source IS TABLE OF rec_source;
m_source t_source;
TYPE l_cursor_type IS REF CURSOR;
l_cursor l_cursor_type;
BEGIN
v_stmt := 'SELECT * FROM temp_table_source where 1 = :x';
v_insert := 'INSERT INTO temp_table_dest (id, name, salary) VALUES (:a, :b, :c)';
OPEN l_cursor FOR v_stmt USING 1; -- example of filtering
LOOP -- use limits to test performance, but the LOOP is optional
FETCH l_cursor BULK COLLECT INTO m_source LIMIT 1000;
--
FORALL i IN 1 .. m_source.COUNT
EXECUTE IMMEDIATE v_insert
USING m_source(i).id, m_source(i).name, m_source(i).salary;
--
COMMIT;
EXIT WHEN m_source.COUNT = 0;
END LOOP;
END;
/
-- See the result
Select count(1) from temp_table_dest;
If you need a dynamic TYPE as well, you will need to create it and drop at the end of your procedure instead of declare it in your package.

Related

how to use collection type in PLSQL, while receiving multiple value from front end java page?

Actually my requirement is,am having 3 tables so i need to create type for each table or consolidate type to all table. but in java developer have to send the multiple parameter to my Procedure, so how to get the multiple values in single type and insert it into table??
Object type
CREATE OR REPLACE TYPE "POL_QUAT_TYPE"
IS OBJECT (V_POLICY_NO VARCHAR2 (30),
V_FOREIGN_POLICY VARCHAR2 (1));
tabletype
CREATE OR REPLACE TYPE POL_QUAT_table IS TABLE OF POL_QUAT_TYPE
Sample procedure
CREATE OR REPLACE PROCEDURE TEST1 (A POL_QUAT_TYPE) IS
B VARCHAR2(100);
C VARCHAR2(100);
BEGIN
B:=A(1);
C:=A(1);
INSERT INTO TEST ( PART, B )
VALUES (B,C);
COMMIT;
END;
Call this procedure
begin
test1('a','b');
end;
I want to know how get the value (a,b) though procedure from collection type.
Ok, you can insert the contents of an array of objects into the table like so:
CREATE OR REPLACE TYPE "POL_QUAT_TYPE"
IS OBJECT (V_POLICY_NO VARCHAR2 (30),
V_FOREIGN_POLICY VARCHAR2 (1));
/
CREATE OR REPLACE TYPE POL_QUAT_table IS TABLE OF POL_QUAT_TYPE;
/
CREATE OR REPLACE PROCEDURE TEST1 (p_a in POL_QUAT_table)
as
BEGIN
INSERT INTO TEST (PART, B)
select t.v_policy_no,
t.v_foreign_policy
from table(p_a) t;
COMMIT;
END;
/
I've created a test case to demonstrate that it works over on Oracle LiveSQL.
This means you can insert all the content of the array in one go without having to loop through the array and insert one record at a time, so it should be much more performant.

PLSQL how to store a result of a select statement

I need to delete data from many tables based on one parameter
The problem is that two tables are related to each other so in order to delete data properly i need to store id's somewhere.
-- i would like to store temp data
-- this one is only for convienience to avoid repeating same select many times
create table ztTaryfa as select zt_taryfa from tw_zbiory_taryfy
where 1=2;
-- this one is mandatory but I dont know how to make it work
Create table wnioskiId as select poli_wnio_id_wniosku from polisy
where 1=2;
Begin
-- fill temp tables
insert into ztTaryfa (
select zt_taryfa from tw_zbiory_taryfy
where zt_zbior = :zbiorId);
insert into wnioskiId (
select poli_wnio_id_wniosku from polisy
where poli_taryfa_id in ztTaryfa);
- regular deletion
delete from POLISY_OT where ot_poli_id in (
select poli_id from polisy
where poli_taryfa_id in ztTaryfa);
commit;
delete from DANE_RAPORTOWE where DR_RPU_ID in (
select RPU_ID from ROZLICZ_PLIK_UBEZP where RPU_ROZLICZ_PLIK_ID in (
select RP_ID from ROZLICZ_PLIK
where RP_ZBIOR_ID = :zbiorId ));
commit;
-- and here we go I need to delete data from POLISY first
delete from POLISY where poli_taryfa_id in ztTaryfa;
commit;
-- but by doing it I lose ids which i need here,
-- so I have to store them somehow and use them here.
delete from WNIOSKI where wnio_id in wnioskiId;
commit;
End;
-- and now lets get rid off temp tables
drop table ztTaryfa;
commit;
drop table wnioskiId;
commit;
To sum up i just need to know how to store somewhere between Begin and End a result of a select query which I can later use in delete statement.
Sounds but I tried so many different methods and all seems to not work.
What u see above is just a 1/3 of the script so I rly would like to make it all simple to use with one parameter.
Thanks you in advance.
You can use global types as simple as this:
create or replace type myrec is object (myid number);
create or replace type mytemp_collection is table of myrec;
declare
v_temp_collection mytemp_collection;
begin
v_temp_collection := mytemp_collection();
select myrec (t.field_type_id ) bulk collect into v_temp_collection from fs_field_types t
where mod(t.field_type_id+1,3)=0; -- for example
FOR i IN 1 .. v_temp_collection.count LOOP
DBMS_OUTPUT.put_line(v_temp_collection(i).myid);
End loop;
delete fs_field_types_back t where t.field_type_id in (select myid from table(v_temp_collection));
end;
Change select and where clause according to your business.

Getting error PLS-00435: DML statement without BULK In-BIND cannot be used inside FORALL

I am getting
Error(68,3): PLS-00435: DML statement without BULK In-BIND cannot be used inside FORALL.
Please help me out with this.
My code is : -
create or replace PACKAGE BODY FIBRE_TOOLS AS
g_package_name varchar2(30):='FIBRE_TOOLS';
g_proc_name varchar2(30);
..
..
..
procedure prc_purge(p_nb_month IN number default 210) is
reqSelec VARCHAR2(4000);
reqDELDES VARCHAR2(4000);
reqDELINS VARCHAR2(4000);
TYPE Curseur IS REF CURSOR;
c_desinscription Curseur;
TYPE selREC IS RECORD (
EMAIL desinscription.EMAIL%type,
IDRA desinscription.IDRA%type,
D_DATE desinscription.desinscription_date%type
);
TYPE selTABLE IS TABLE OF selREC;
ListeFIB selTABLE;
BEGIN
reqSelec :='select EMAIL,IDRA,desinscription_date from desinscription where desinscription_date < trunc(add_months(sysdate,-'||p_nb_month||'))';
reqDELDES := 'DELETE FROM DESINSCRIPTION WHERE EMAIL=:1 AND IRDA=:2';
reqDELINS := 'DELETE FROM INSCRIPTION WHERE EMAIL=:1 AND IDRA=:2 AND INSCRIPTION_DATE < TRUNC(:3)';
prc_log('Begining of purging procedure');
open c_desinscription for reqSelec;
LOOP
fetch c_desinscription bulk collect into ListeFIB LIMIT 10000;
EXIT WHEN ListeFIB.count = 0;
FORALL i in ListeFIB.first.. ListeFIB.last
EXECUTE IMMEDIATE reqDELDES USING ListeFIB.EMAIL,ListeFIB.IRDA;
EXECUTE IMMEDIATE reqDELINS USING ListeFIB.EMAIL,ListeFIB.IDRA,ListeFIB.D_DATE;
COMMIT;
EXIT WHEN c_desinscription%NOTFOUND;
END LOOP;
close c_desinscription;
COMMIT;
prc_log('Ending of purging procedure');
end prc_purge;
end FIBRE_TOOLS;
I am trying to delete data from two tables based on the two columns getting selected on a criteria I.e. nb_months.
I think problem is with table type and record type. I am confused which way it can be done. As per my knowledge record type shall be used with FORALL. kindly help me on this, as it is very critical.
You're using a DML statement with a FORALL but without binding the collection used - Oracle doesn't allow for that.
Replace execute immediates with the binded collection, should work
EXECUTE IMMEDIATE reqDELDES USING ListeFIB(i).EMAIL,ListeFIB(i).IRDA;
EXECUTE IMMEDIATE reqDELINS USING ListeFIB(i).EMAIL,ListeFIB(i).IDRA,ListeFIB(i).D_DATE;
See more examples in these articles:
Oracle Magazine
Oracle Documentation

how to insert the value into a table type through a select statement in oracle?

I am a beginner in Oracle. I created the table type as like as follows:
TYPE metertable IS TABLE OF pseb.metermaster.meterid%type;
I dnt know how to insert the value into that table type. I need to store the whole resultset of the following query into the table type.
select distinct(meterid)
from pseb.consumerfeedermetermapper
where feederid in (select distinct (fm.FeederID)
from pseb.feedermaster fm,pseb.consumerfeedermetermapper cfm
where fm.substationid=v_v_type
and cfm.feederid=fm.feederid
and cfm.FeederID>0)
and meterid >0
order by meterid;
Help me to do that.
Use BULK COLLECT to select the data into a variable of that type:
declare
mt metertable;
begin
select distinct(meterid)
bulk collect into mt
from pseb.consumerfeedermetermapper
where feederid in (select distinct (fm.FeederID)
from pseb.feedermaster fm,pseb.consumerfeedermetermapper cfm
where fm.substationid=v_v_type
and cfm.feederid=fm.feederid
and cfm.FeederID>0)
and meterid >0
order by meterid;
-- Now use mt...
end;
I had the same issue, and this code help me :
SET SERVEROUTPUT ON
DECLARE
TYPE t_bulk_collect_test_tab IS TABLE OF bulk_collect_test%ROWTYPE;
l_tab t_bulk_collect_test_tab;
l_cursor SYS_REFCURSOR;
BEGIN
-- Way 1
OPEN l_cursor FOR 'SELECT * FROM bulk_collect_test';
FETCH l_cursor
BULK COLLECT INTO l_tab;
CLOSE l_cursor;
DBMS_OUTPUT.put_line('Dynamic FETCH : ' || l_tab.count);
-- Way 2
EXECUTE IMMEDIATE 'SELECT * FROM bulk_collect_test' BULK COLLECT INTO l_tab;
DBMS_OUTPUT.put_line('Dynamic EXECUTE: ' || l_tab.count);
END;
/
http://www.dba-oracle.com/plsql/t_plsql_dynamic.htm
declare
type tab_type is table of consumerfeedermetermapper%rowtype;
tab_t tab_type;
begin
select distinct(meterid) bulk collect into tab_t
from pseb.consumerfeedermetermapper
where feederid in (select distinct (fm.FeederID)
from pseb.feedermaster fm,pseb.consumerfeedermetermapper cfm
where fm.substationid=v_v_type
and cfm.feederid=fm.feederid
and cfm.FeederID>0)
and meterid >0
order by meterid;
end;
You can use the above code

Select from PLSQL Associative array?

Is it possible to use SELECT FROM when using an associative array? I'm passing an array to a stored procedure through a .NET application, and I wanna be able to use that array as a condition when selecting from another table. Lets say I'm passing an array of IDs to the procedure, I wanna be able to do this:
select * from table1 where userID in (select column_value from array)
The type for the array is defined in the package:
type id_array is type of number index by pls_integer
Yes, it is possible, by wrapping the array with a pipelined function. Here's a good primer on pipelined functions:
http://www.oracle-developer.net/display.php?id=429
UPDATE: Oracle 12c now supports querying associative arrays using the TABLE operator, as long as the type is declared in a package spec: https://galobalda.wordpress.com/2014/08/02/new-in-oracle-12c-querying-an-associative-array-in-plsql-programs/
e.g.
select * from table1
where userID in (select column_value from table(array));
No, you can't select from PL/SQL arrays, since you use SQL in select from statements, though you can use DB defined Nested Tables types in SQL. This short article can help you get started.
Take a look a this simple synthetic exmple:
> create type temp_t as table of int;/
Type created.
> select 'test' from dual where 1 in (select * from table(temp_t(1,2,3)));
'TES
----
test
An example using PLSQL (to select from a nested table):
create type temp_r as OBJECT(
temp_varchar2 varchar2(100),
temp_number number(20)
);
/
create type temp_t as TABLE of temp_r;
/
set serveroutput on size 1000000
/
-- PLSQL starts here
declare
temp_rec temp_r := temp_r(null, null); -- empty constructor to initialize object
temp_table temp_t := temp_t(); -- empty constructor to initialize object
lv_ref_cursor SYS_REFCURSOR;
lv_temp_varchar2 varchar(100);
lv_temp_number number(20);
begin
temp_rec.temp_varchar2 := 'first';
temp_rec.temp_number := 1;
temp_table.extend;
temp_table(1) := temp_rec;
temp_table.extend;
temp_table(2) := temp_r('second', 2);
OPEN lv_ref_cursor FOR
SELECT temp_varchar2, temp_number
FROM table(temp_table)
where temp_number = 1;
fetch lv_ref_cursor into lv_temp_varchar2, lv_temp_number;
close lv_ref_cursor;
dbms_output.put_line('returns: ' || lv_temp_varchar2 || ', ' || lv_temp_number);
end;
/

Resources