Collect multiple rows and store values in PL/SQL - oracle

I have the following query:
DECLARE
type INV_TASK_GLOBAL is varray(10) of NUMBER(20,0);
type STUDY_CASE_GLOBAL is varray(10) of NUMBER(20,0);
BEGIN
SELECT T_INVESTIGATIONTASK.ID, T_STUDYCASE.ID
into INV_TASK_GLOBAL, STUDY_CASE_GLOBAL
FROM T_VALIDATIONCARRIER
left join T_ESTIMATIONOBJECT on T_VALIDATIONCARRIER.IA_ESTIMATIONOBJECT_ID = T_ESTIMATIONOBJECT.ID
left join T_INVESTIGATIONTASK on T_ESTIMATIONOBJECT.INVESTIGATIONTASK_ID = T_INVESTIGATIONTASK.ID
left join T_STUDYCASE on T_INVESTIGATIONTASK.STUDYCASE_ID = T_STUDYCASE.ID
WHERE T_VALIDATIONCARRIER.ESTIMATIONOBJECT_ID = 940;
dbms_output.Put_line('INVESTIGATIONTASK_ID: ' || INV_TASK_GLOBAL);
dbms_output.Put_line('STUDYCASE_ID: ' || STUDY_CASE_GLOBAL);
END;
The compiler is telling me that the number specified in exact fetch is less than the rows returned. The fact is that I want those lines to be returned. To be specific: I would like to collect all the T_INVESTIGATIONTASK.ID and T_STUDYCASE.ID (one per each row which is captured by the WHERE clause), store them into the INV_TASK_GLOBAL and in STUDY_CASE_GLOBAL and then display all the values returned (separated maybe by a comma).
I may change the WHERE condition in the future but the maximum number of elements I expect to be returned for both variables is 10 anyway.
I know that I am using the varray datatype in a wrong way: I need some sort of cycle and a list/array datatype to store all the returned values in the INV_TASK_GLOBAL and STUDY_CASE_GLOBAL variables and then print the array on screen. Do you have any idea of how to accomplish it?

After some tests the following code solved my problem:
DECLARE
type collection_id is table of NUMBER(20,0);
INV_TASK_GLOBAL collection_id := collection_id(10);
STUDY_CASE_GLOBAL collection_id := collection_id(10);
BEGIN
SELECT T_INVESTIGATIONTASK.ID, T_STUDYCASE.ID
BULK COLLECT into INV_TASK_GLOBAL, STUDY_CASE_GLOBAL
FROM T_VALIDATIONCARRIER
left join T_ESTIMATIONOBJECT on T_VALIDATIONCARRIER.IA_ESTIMATIONOBJECT_ID = T_ESTIMATIONOBJECT.ID
left join T_INVESTIGATIONTASK on T_ESTIMATIONOBJECT.INVESTIGATIONTASK_ID = T_INVESTIGATIONTASK.ID
left join T_STUDYCASE on T_INVESTIGATIONTASK.STUDYCASE_ID = T_STUDYCASE.ID
WHERE T_VALIDATIONCARRIER.ESTIMATIONOBJECT_ID = 940;
FOR indx IN 1 .. INV_TASK_GLOBAL.COUNT
LOOP
dbms_output.Put_line('INVESTIGATIONTASK_ID: ' || INV_TASK_GLOBAL(indx));
END LOOP;
FOR indx IN 1 .. STUDY_CASE_GLOBAL.COUNT
LOOP
dbms_output.Put_line('STUDY_CASE_ID: ' || STUDY_CASE_GLOBAL(indx));
END LOOP;
END;

Related

Oracle nested cursor with for loop

I am trying to make this work but no matter what I do the od_total var is always empty. I just cannot make it work.
I am trying to run a cursor to get a list of ordersid and save it into o_id every loop. Then, I use the second cursor to use the value stored into o_id to do another query that stores the result into od_total. While o_id seems to be storing the values, od_total for some reason is always empty.
CREATE OR REPLACE PROCEDURE p_practicum2_practice IS
o_id CHAR(5);
od_total NUMBER := 0;
CURSOR ordersid IS
SELECT DISTINCT orderdetails.orderid FROM orderdetails
INNER JOIN orders ON orderdetails.orderid = orders.orderid
INNER JOIN customers ON orders.customerid = customers.customerid
WHERE orders.customerid = 'LILAS';
CURSOR total IS
SELECT SUM(unitprice*quantity) FROM orderdetails WHERE orderid = o_id;
BEGIN
OPEN ordersid;
OPEN total;
LOOP
FETCH ordersid INTO o_id;
EXIT WHEN ordersid%notfound;
LOOP
FETCH total INTO od_total;
EXIT WHEN total%notfound;
END LOOP;
dbms_output.put_line(o_id || ' ---- ' || od_total);
END LOOP;
CLOSE ordersid;
CLOSE total;
END;
this is what I get as result:
enter image description here
As far i could understand your question you wanted to display od_total for all od_id passed from cursor ordersid. You are not getting anything because od_id is not getting passed to your second cursor. Read the code below inline comments how to pass the argument to cursor.
CREATE OR REPLACE PROCEDURE p_practicum2_practice
IS
o_id CHAR(5);
od_total NUMBER := 0;
CURSOR ordersid
IS
SELECT DISTINCT orderdetails.orderid
FROM orderdetails
INNER JOIN orders
ON orderdetails.orderid = orders.orderid
INNER JOIN customers
ON orders.customerid = customers.customerid
WHERE orders.customerid = 'LILAS';
CURSOR total(v_oid char)
IS
SELECT SUM(unitprice*quantity)
FROM orderdetails
WHERE orderid = v_oid;
BEGIN
OPEN ordersid;
LOOP
FETCH ordersid INTO o_id;
EXIT WHEN ordersid%notfound;
OPEN total(o_id); --<--This is how you pass the paramater to your cursor
LOOP
FETCH total INTO od_total;
EXIT WHEN total%notfound;
dbms_output.put_line(o_id || ' ---- ' || od_total);
END LOOP;
CLOSE total;
--If you print here then only the last od_total value will be printed.
--dbms_output.put_line(o_id || ' ---- ' || od_total);
END LOOP;
CLOSE ordersid;
END;
Output:
17465 ---- 6
12345 ---- 3
14435 ---- 4
19045 ---- 6
19345 ---- 8

Error in pl/sql block has me stuck - Number and/or types of columns in a query does not match

Not quite sure what I am doing wrong here, went over the code a few times tonight and I think I need a fresh set of eyes. I keep getting an error that states:
*Cause: Number and/or types of columns in a query does not match declared
return type of a result set variable, or declared types of two Result
Set variables do not match.
*Action: Change the program statement or declaration. Verify what query the variable
actually refers to during execution.
DECLARE
cv_prod SYS_REFCURSOR;
rec_payment dd_payment%ROWTYPE;
TYPE pay2 IS RECORD (
pledge_id dd_pledge.idpledge%TYPE,
amount NUMBER(8,2)
);
rec_payment2 pay2;
lv_donor_id dd_donor.iddonor%TYPE := 303;
lv_indicator_value CHAR(1) := 'd';
BEGIN
IF lv_indicator_value = 'd' THEN
OPEN cv_prod FOR
SELECT idpledge, payamt, paydate, paymethod
FROM dd_pledge inner join dd_payment USING (idpledge)
WHERE iddonor = lv_donor_id
ORDER BY idpledge, paydate;
LOOP
FETCH cv_prod INTO rec_payment;
EXIT WHEB cv_prod%NOTFOUND;
dbms_output.put_line(rec_payment.idpledge || ' ' || rec_payment.payamt || ' ' || rec_payment.paydate || ' ' || rec_payment.paymethod);
END LOOP;
ELSIF Lv_Indicator_Value = 's' THEN
OPEN cv_Prod FOR
SELECT idpledge, payamt, paydate, paymethod
FROM dd_pledge INNER JOIN dd_payment USING (idpledge)
WHERE iddonor = lv_donor_id
GROUP BY idpledge;
LOOP
FETCH cv_prod INTO rec_payment2;
EXIT WHEN cv_prod%NOTFOUND;
dbms_output.put_line(rec_payment2.pledge_id || ' ' || rec_payment2.amount);
END LOOP;
END IF;
END;
You're collecting the fields selected by your query into a record which doesn't have the same number and type of fields of your query.
For example, in your second cursor you're selecting the fields idpledge, payamt, paydate, paymethod:
OPEN cv_prod FOR
SELECT idpledge, payamt, paydate, paymethod
FROM dd_pledge INNER JOIN dd_payment USING (idpledge)
WHERE iddonor = lv_donor_id
GROUP BY idpledge;
And you're trying to put them into the rec_payment2 record:
FETCH cv_prod INTO rec_payment2;
But the type of this record is pay2, which is a record with only two fields:
TYPE pay2 IS RECORD (
pledge_id dd_pledge.idpledge%TYPE,
amount NUMBER(8,2)
);

Using Rownum in Cursor Bulk Collect Oracle

I'm trying to use the rownum to simulate a column autonumbered as I need to use it as an ID. Since it is an ID, I look at the final table if no record with MAX (ID).
The problem I have is when I want to do arithmetic operations within the cursor or when you invoke, or when you want to use a function. The ROWNUM (v_id) field is empty me when I want to print with DBMS_OUTPUT . Anyone have any idea how to solve it without using sequences ?
Here put the sample code.
declare
max_id number;
CURSOR INSRT(w_max number) IS
SELECT f_max_fact_sap(to_number(V_ID),w_max) AS V_ID,Seriei,serief
FROM (SELECT To_Char(ROWNUM) AS V_ID, A.*
FROM (SELECT DISTINCT a.matnr, a.seriei, a.serief,a.xblnr,a.fecha_sap, ((SERIEF-SERIEI)+1) AS rango
FROM SOPFUN.TT_ZMOVIMI_FACTURADAS a
WHERE 0 =(SELECT COUNT(1)
FROM PA_ZMOVIMI_FACTURADAS B
WHERE A.SERIEI = B.SERIEI
AND A.SERIEF = B.SERIEF
AND A.MATNR = B.MATNR
AND A.FECHA_SAP=B.FECHA_SAP)
AND A.FECHA_SAP IS NOT NULL) A);
TYPE T_INSRT IS TABLE OF INSRT%ROWTYPE INDEX BY PLS_INTEGER;
V_INSRT T_INSRT;
begin
SELECT Max(Nvl(ID,10000)) INTO MAX_ID-- To Proof because the table is empty
FROM PA_ZMOVIMI_FACTURADAS;
OPEN INSRT(MAX_ID);
LOOP
FETCH INSRT BULK COLLECT INTO V_INSRT LIMIT 1000;
FOR I IN 1 .. V_INSRT.Count loop
DBMS_OUTPUT.PUT_LINE('ID: ' ||V_INSRT(I).V_ID||' SI: '||V_INSRT(I).SERIEI||' SI: '||V_INSRT(I).SERIEF||' OPERACION: '||to_char(f_max_fact_sap(V_INSRT(I).V_ID,MAX_ID)));
end loop;
EXIT WHEN INSRT%NOTFOUND;
END LOOP;
end;

bind array in cursor using plsql

declare
cursor lc is
select *
from (select a.lin, a.pr,
b.name, sum(a.up) as u,
sum (a.d) as d
from li_dy_4 a,
p_list b
where a.pr=b.id
and b.parent_id != 0
and a.partitionid <= 308
and a.partitionid >= 302
and a.pr in (91,80)
GROUP BY a.pr, b.name, a.lin
order by d desc) ;
rec lc%ROWTYPE;
BEGIN
open lc;
loop
FETCH lc into rec;
dbms_output.put_line(rec.pr);
exit when lc%NOTFOUND;
end loop;
close lc;
END;
the above statement works fine for me. What I am not capable of finding anything hopeful is changing the value after the "in" statement which is a.pr in (91,80)
I have listed the values here manually, but I want to pass it to the cursor as an array of numbers for a.pr column. In short I want to do a.pr = idlist wher idlist is an array. Please anyone tell me if my idea is possible.
Just want to remind you, the IN clause supports 1000 items only. And that could be the primary reason ,there's nothing called BULK BINDING for SELECT Queries. We have FORALL INSERT/UPDATE, which is like BULK BINDING. Unfortunately select has none.
But still you can achieve your requirement in a different fashion.
You can try a global temporary table(GTT) which a temporary table with "scope of the data inserted" is only to that session.
You can FORALL INSERT all your data for IN clause into that table, and join the TABLE to your Query.
Else you can have a nested table (if oracle 10g) or a simple pl/sql type itself (if oracle 11g), with all your IN class items as records and join it to your Query.
Example: Using NESTED TABLE , effective for less number(<10000) of items
CREATE TYPE pr AS OBJECT
(pr NUMBER);
/
CREATE TYPE prList AS TABLE OF pr;
/
declare
myPrList prList := prList ();
cursor lc is
select *
from (select a.lin, a.pr,
b.name, sum(a.up) as u,
sum (a.d) as d
from li_dy_4 a,
p_list b,
TABLE(CAST(myPrList as prList)) my_list
where a.pr=b.id
and b.parent_id != 0
and a.partitionid <= 308
and a.partitionid >= 302
and a.pr = my_list.pr
GROUP BY a.pr, b.name, a.lin
order by d desc) ;
rec lc%ROWTYPE;
BEGIN
/*Populate the Nested Table, with whatever collection you have */
myPrList := prList ( pr(91),
pr(80));
/*
Sample code: for populating from your TABLE OF NUMBER type
FOR I IN 1..your_input_array.COUNT
LOOP
myPrList.EXTEND;
myPrList(I) := pr(your_input_array(I));
END LOOP;
*/
open lc;
loop
FETCH lc into rec;
exit when lc%NOTFOUND; -- Your Exit WHEN condition should be checked afte FETCH iyself!
dbms_output.put_line(rec.pr);
end loop;
close lc;
END;
/
I don't know the exact structure of your global table but you can use the collection in cursor like this
declare
cursor c1 is
select last_name ls from empc;
type x is table of employees.last_name%type;
x1 x := x();
cnt integer :=0;
begin
for z in c1 loop
cnt := cnt +1;
x1.extend;
x1(cnt) := z.ls;
if x1(cnt) is NULL then-----------
DBMS_OUTPUT.PUT_LINE('ASHISH');
end if;
dbms_output.put_line(cnt || ' '|| x1(cnt));
end loop;
end;

Sequence Number is not allowed in Explicit Cursors

I can't use a sequence in a Explicit Cursors.
Error: PL/SQL:ORA-02287: Sequence number not allowed here
Code:
CURSOR c_service_consumer IS
select ops$KLI.SVC_SEQ.Nextval,
adr_cli_id_s,
null,
svc_srv_id_s
....
I tried to use like this:
CURSOR c_service_consumer IS
select SELECT ops$KLI.SVC_SEQ.Nextval FROM DUAL,
adr_cli_id_s,
null,
svc_srv_id_s
And is the same error.
This is the Cursor:
TYPE l_service_consumer_row IS TABLE OF TA_MAIN.SERVICE_CONSUMER%ROWTYPE;
l_service_consumer l_service_consumer_row;
CURSOR c_service_consumer IS
select ops$KLI.SVC_SEQ.Nextval,
a.adr_cli_id_s,
svc_prp_id_s,
svc_srv_id_s
from ops$luan.be_1304_grain_adr a
left outer join ta_main.clients c on (a.adr_cli_id_s = c.cli_id_s);
BEGIN
OPEN c_service_consumer;
FETCH c_service_consumer BULK COLLECT INTO l_service_consumer;
CLOSE c_service_consumer;
/*Insert the collection of values*/
FORALL i IN INDICES OF l_service_consumer SAVE EXCEPTIONS
INSERT INTO TA_MAIN.SERVICE_CONSUMER
VALUES l_service_consumer(i);
Hope this helps (not tested)
TYPE l_service_consumer_row IS TABLE OF TA_MAIN.SERVICE_CONSUMER%ROWTYPE
INDEX BY BINARY_INTEGER;
l_service_consumer l_service_consumer_row;
fetch_size NUMBER := 5000; -- scale the value
CURSOR c_service_consumer IS
select -1 id,
a.adr_cli_id_s,
svc_prp_id_s,
svc_srv_id_s
from ops$luan.be_1304_grain_adr a
left outer join ta_main.clients c on (a.adr_cli_id_s = c.cli_id_s);
BEGIN
OPEN c_service_consumer;
loop
FETCH c_service_consumer BULK COLLECT INTO l_service_consumer LIMIT fetch_size;
FOR i IN 1 .. l_service_consumer.COUNT LOOP
l_service_consumer(i).id = ops$KLI.SVC_SEQ.Nextval;
END LOOP;
/*Insert the collection of values*/
FORALL i IN INDICES OF l_service_consumer SAVE EXCEPTIONS
INSERT INTO TA_MAIN.SERVICE_CONSUMER
VALUES l_service_consumer(i);
EXIT WHEN c_service_consumer%NOTFOUND;
END LOOP;
CLOSE c_service_consumer;
End;

Resources