PLSQL FOR loop while executing CURSOR - plsql

I would like to know if there's any option to iterate a table while performing SELECT values into a CURSOR.
For example:
I have a table TEMP_NUMBERS which contains only numbers (single column).
I have to perform a SELECT from each number in the table (I do not know the amount of rows in the table in advance).
Here is basically what I'm attempting to do. Obviously this does not work, but can I do some kind of a workaround?
I need to SELECT the data into the p_cv_PermsNotifs which is a RETURN REF CURSOR.
IF NOT p_cv_PermsNotifs%ISOPEN THEN OPEN p_cv_PermsNotifs FOR
FOR i IN 1..TEMP_NUMBERS.NUMBER.COUNT LOOP
SELECT DISTINCT
SEC_USER_ROLE.ENTITY_TYP_CODE,
SEC_USER_ROLE.ENTITY_ID
FROM
SEC_USER_ROLE
WHERE
SEC_USER_ROLE.ENTITY_ID = i
END LOOP;
END IF;
Also tried this:
IF NOT p_cv_PermsNotifs%ISOPEN THEN OPEN p_cv_PermsNotifs FOR
SELECT DISTINCT
SEC_USER_ROLE.ENTITY_TYP_CODE,
SEC_USER_ROLE.ENTITY_ID
FROM
SEC_USER_ROLE
WHERE
SEC_USER_ROLE.ENTITY_ID IN
(SELECT * FROM TABLE (lv_ListOfEntities))
END IF;
Where lv_ListOfEntities is table of NUMBER indexed by BINARY INTEGER.
But I'm getting "ORA-22905: cannot access rows from a non-nested table item"
Thanks in advance.

In> Hey if you pass a single number at a time, everytime the refcursor
will be overwritten by the next value. So at the end you will only get
the value for last number in the refcursor. A better way is to use
some basic PL/SQL Bulk COLLECT logic which will give you the desired
output.
Hope this helps
--Creating sql type
CREATE OR REPLACE TYPE lv_num_tab IS TABLE OF NUMBER;
--plsql block
var p_lst refcursor;
DECLARE
lv_num lv_num_tab;
BEGIN
SELECT COL1 BULK COLLECT INTO lv_num FROM TEMP_NUMBERS;
OPEN p_lst FOR
SELECT DISTINCT SEC_USER_ROLE.ENTITY_TYP_CODE,
SEC_USER_ROLE.ENTITY_ID
FROM SEC_USER_ROLE
WHERE SEC_USER_ROLE.ENTITY_ID IN
(SELECT * FROM TABLE(cast(lv_num as lv_num_tab))
);
END;

Related

MariaDB Stored Procedure store paramters for update

I am trying to write a MariaDB stored procedure.
Due to SQL_SAFE_UPDATES, it is required to use the ID column to use in the WHERE clause for updates. Due to this, what is the normal approach to also select a value from one of the other columns? I do not want to have multiple SELECT statements as it seems inefficient and room for error because they could return values from different rows.
I would like to store my first select statement
SELECT id, sequence FROM RECORDSEQUENCE WHERE SEQTABLE = SeqTable;
In the following two parameters #id, #seq from two seperate columns in the above query and use them in the UPDATE statement as well as the IF statement.
CREATE DEFINER=`sd`#`%` PROCEDURE `SD_GenerateNextRecordSequence`(IN SeqTable int)
BEGIN
SELECT id, sequence FROM RECORDSEQUENCE WHERE SEQTABLE = SeqTable;
IF (#seq IS NOT NULL) THEN
SET #NEXTSEQ := #seq+1;
UPDATE RECORDSEQUENCE SET RECORDSEQUENCE = #NEXTSEQ WHERE id = #id;
ELSE
SET #NEXTSEQ := 100;
INSERT INTO RECORDSEQUENCE (RECORDSEQUENCE,SEQTABLE) VALUES (#NEXTSEQ,SeqTable);
END IF;
SELECT #NEXTSEQ as SEQUENCE;
END

id invalid column in cursor though my table has column id

My table has a defined column id.
Though the same query runs fin e independently,but when I try to declare it in a cursor, it shows invalid column.
declare
type PA_1 is record (PI number);
calc number;
row_container PA_1;
begin
for row_container in
(
select distict t1.pi , t2.id
from table1 t1, table2 t2
where t1.Pi=t2.pi
);
Loop
select calculation to calc
from table1 t1
where t1.pi=row_container.pi and t2.id=row_container.id;
end loop;
commit;
end;
the inner query runs fine otherwise. Please help
Several syntax errors:
First of all, you should remove the declaration for row_container. for row_container in () makes this inplicitely (and your declaration with one column doesn't even match your query with two columns).
distict should be distinct.
Then remove the semicolon before the Loop keyword. It doesn't belong there.
Then select calculation to calc should be select calculation into calc.
Inside the loop you select from table1 (which you call t1 again), but your where clause contains t2.id, while there is no t2 in that query.
And then: What is this routine supposed to do? It selects some value into the variable calc, but doesn't use it. So once it runs, it just doesn't do anything.

PL/SQL List of items, check if record exists, if yes update if not create

Im trying to learn PL/SQL and I was given an assignment which I am not sure how to tackle.
I am given a list of orders. I want to check my ORDER table for each of them in the following way:
Check if order exists, if no create a record
Check if order fullfilled (0 or 1)
If order is not fullfilled (0), update to 1
I put together a script which I think can do this for one order, but I'm sure it's not very good:
DECLARE
tmp NUMBER;
tmp2 NUMBER;
o_id NUMBER := 999;
BEGIN
/*Checking if order exists */
SELECT COUNT (*)
INTO tmp
FROM ORDERS
WHERE ORDERID = o_id;
IF ( tmp = 0 ) THEN
/* INSERT HERE */
END IF;
SELECT FULLFILLED INTO tmp2
FROM ORDERS
WHERE ORDERID = o_id;
IF (tmp2 = 0) THEN
/* UPDATE... */
END IF;
end;
I would appreciate any advice, what should I look into to make this script efficient? Thank you.
MERGE statement is what you need. It is based on SELECT statement and let's you UPDATE or INSERT data using it's WHEN (NOT) MATCHED THEN clauses. Here's a good explanation with some examples: Oracle Base MERGE Statement.
Here's also some code snippet you might find useful:
DECLARE
o_id NUMBER := 999;
BEGIN
MERGE INTO ORDERS o
USING
(SELECT o_id AS orderid FROM dual) o_id
ON
(o.orderid = o_id.orderid)
WHEN MATCHED THEN
UPDATE SET
o.fulfilled = CASE WHEN o.fulfilled = 0 THEN 1 ELSE o.fulfilled END
WHEN NOT MATCHED THEN
INSERT (fulfilled, <some_other_columns>)
VALUES (1, <values_for_other_columns>);
END;
/
Please read up on the merge statement: https://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016.htm
Also called an "upsert". Basically if the row does not exist, insert. If it does, update.
It does what you are trying to do in one statement.

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.

PL/SQL - LAST_VALUE return more than one row?

I am doing an school assigment where I need to get the last value of "code" so I can then insert next row with this code incremented. I tried to pull it out this way.
DECLARE
v_last_code f_shifts.code%TYPE;
BEGIN
SELECT LAST_VALUE(code) OVER (ORDER BY code)
INTO v_last_code
FROM f_shifts;
DBMS_OUTPUT.PUT_LINE('Last value is: ' || v_last_code);
END;
However I get ORA-01422: exact fetch returns more than one requested number of rows
and I have no idea why and how can a last_value be more than one row
Thanks !
You can use a nested table like this.
DECLARE
v_last_code f_shifts.code%TYPE;
TYPE t_tbl IS TABLE OF f_shifts.code%TYPE;
-- Above line creates the nested table type of the required type.
v_tbl T_TBL;
-- Above line creates the nested table variable.
BEGIN
SELECT code
BULK COLLECT INTO v_tbl -- collects all the values, ordered, into the nested table
FROM f_shifts
ORDER BY code;
v_last_code = v_tbl(v_tbl.LAST); -- gets the last values and puts into the variable
DBMS_OUTPUT.PUT_LINE('Last value is: ' || v_last_code);
END;
/

Resources