Select Query in cursor FOR LOOP - oracle

Pretty new to Oracle, need help on this Procedure, using Oracle 11g
Sample table data (TAB1):
ID
Amount
Currency
10
300
GBP
15
500
GBP
20
100
GBP
Requirement is to select all the ID's from TAB1 based on currency and store it in a variable and later use these ID's in other select Queries within the same Stored Procedure.
CREATE OR REPLACE PROCEDURE myproc (i_id IN VARCHAR, i_curr IN VARCHAR)
AS
CURSOR GET_I IS
SELECT ID, CURRENCY
FROM TAB1
WHERE CURRENCY = 'GBP';
-- This will give me 3 ID's (10, 15 & 20) which I am storing in variable r_1 below.
r_1 VARCHAR (5) : NULL;
BEGIN
OPEN GET_I;
LOOP
FETCH GET_I INTO r_1;
IF GET_I%NOTFOUND
THEN
EXIT;
---In the below ELSE PART can we run a select query using the value stored in r_1??
--ELSE
--Data stored in r_1 to be used in further select queries in later part and output of the below
--be returned as SYS_REFCURSOR;
--BELOW two lines gives error
--FOR I in r_1 (
--SELECT ID FROM TAB2 WHERE TAB2.ID=r_1);
END IF;
END LOOP;
CLOSE GET_ID;
END;

It looks you want to use a nested FOR loop.
I'd suggest you to use cursor FOR loops - they are easier to maintain as you don't have to declare cursor variable (by the way, in your case it wouldn't work anyway as you'd like to store both ID and CURRENCY into a scalar R_1 variable), open the cursor, pay attention about exiting the loop and closing the cursor. In a cursor FOR loop, Oracle does all that for you.
Here's an example:
Sample table:
SQL> select * from tab1;
ID AMOUNT CUR
---------- ---------- ---
10 300 GBP
15 500 GBP
20 100 GBP
Procedure:
SQL> create or replace procedure myproc as
2 begin
3 for cur_id in (select id from tab1) loop
4 dbms_output.put_line('ID = ' || cur_id.id);
5 for cur_other in (select amount, currency
6 from tab1
7 where id = cur_id.id --> use ID fetched in outer loop
8 )
9 loop
10 dbms_output.put_line(cur_other.amount ||' - '|| cur_other.currency);
11 end loop;
12 end loop;
13 end;
14 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec myproc;
ID = 10
300 - GBP
ID = 15
500 - GBP
ID = 20
100 - GBP
PL/SQL procedure successfully completed.
SQL>
How to return a refcursor?
SQL> create or replace procedure myproc (par_id in tab1.id%type,
2 par_rc out sys_refcursor) as
3 begin
4 for cur_id in (select id
5 from tab1
6 where id = par_id
7 ) loop
8
9 open par_rc for select amount, currency
10 from tab1
11 where id = cur_id.id;
12 end loop;
13 end;
14 /
Procedure created.
SQL> var l_rc refcursor
SQL>
SQL> exec myproc(10, :l_rc);
PL/SQL procedure successfully completed.
SQL> print l_rc
AMOUNT CUR
---------- ---
300 GBP
SQL>

Related

How can I create an array in PL/SQL that takes values from a SELECT statement?

I'm trying to create a script that can select a list of values, put them into an array, then iterate the array so that the selected values can be deleted. I think I'm ultimately just confusing myself, but my biggest issue seems to be figuring out how to select values into an array. With every other example I've seen on here, people define the array statically, not with a select statement.
I've tried to create a FOR loop that looks like this:
DECLARE
type PERMIDS IS VARRAY(10) OF VARCHAR2(10);
ID PERMIDS;
total integer;
BEGIN
ID := PERMIDS('1','2','3','4','5');
total := ID.count;
FOR i in 1 .. total LOOP
SYS.DBMS_OUTPUT.PUT_LINE('ID: ' || ID(i));
END LOOP;
END;
This runs, but it doesn't allow for dynamic definition of the array.
What I'd like to do is something like this, with the select statement potentially yielding any number of results:
DECLARE
type PERMIDS IS VARRAY(10) OF VARCHAR2(10);
ID PERMIDS;
total integer;
BEGIN
SELECT rg.ID INTO ID
FROM table1 rg
LEFT JOIN table2 a
ON rg.ID = a.ID
WHERE a.ID = '1234';
total := ID.count;
FOR i in 1 .. total LOOP
SYS.DBMS_OUTPUT.PUT_LINE('ID: ' || ID(i));
END LOOP;
END;
It's important that this loop can handle any number of results from the select statement, 0 included, as a user could have any number of records.
One option is to use built-in sys.odcivarchar2list type and bulk collect into it.
Sample table (department names will be put into a collection):
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
PL/SQL procedure:
SQL> set serveroutput on
SQL>
SQL> declare
2 l_id sys.odcivarchar2list;
3 begin
4 select dname
5 bulk collect into l_id
6 from dept;
7
8 for i in 1 .. l_id.count loop
9 dbms_output.put_line('ID = ' || l_id(i));
10 end loop;
11 end;
12 /
ID = ACCOUNTING
ID = RESEARCH
ID = SALES
ID = OPERATIONS
PL/SQL procedure successfully completed.
SQL>

How to update ref cursor values in oracle?

I want to fetch limited no. of rows using refcursor. then I need to update same set of records. is it possible?
create or replace PROCEDURE myproc (
P_ROWCOUNT IN NUMBER,
OUT_TXN_IDS OUT OF_CR_TYPE,
P_CD_ERROR OUT NUMBER,
P_DS_ERROR OUT VARCHAR2
)
AS
V_TXNID NUMBER;
BEGIN
P_CD_ERROR := 0;
P_DS_ERROR := 'SUCCESS';
OPEN OUT_TXN_IDS for
SELECT id FROM table1 WHERE status='N' AND ROWNUM<=P_ROWCOUNT;
EXCEPTION
WHEN OTHERS THEN
P_CD_ERROR := sqlcode;
P_DS_ERROR := 'WF-ERROR - myproc - ' || substr(SQLERRM, 1, 200);
RETURN;
END myproc;
I need to update same records to status Y after refcursor returns. can we do this. please suggest
I don't have your tables nor data so I simplified it a little bit, but - it should work nonetheless.
Initial statuses:
SQL> SELECT status, count(*) FROM table1 group by status;
S COUNT(*)
- ----------
Y 7
N 7
Procedure: basically, you'd modify rows represented by ID returned by ref cursor.
SQL> DECLARE
2 out_txn_ids SYS_REFCURSOR;
3 p_rowcount NUMBER := 5;
4 l_id table1.id%TYPE;
5 BEGIN
6 OPEN out_txn_ids FOR SELECT id
7 FROM table1
8 WHERE status = 'N'
9 AND ROWNUM <= p_rowcount;
10
11 LOOP
12 FETCH out_txn_ids INTO l_id;
13
14 EXIT WHEN out_txn_ids%NOTFOUND;
15
16 UPDATE table1
17 SET status = 'Y'
18 WHERE id = l_id;
19 END LOOP;
20
21 CLOSE out_txn_ids;
22 END;
23 /
PL/SQL procedure successfully completed.
Result:
SQL> SELECT status, count(*) FROM table1 group by status;
S COUNT(*)
- ----------
Y 12
N 2
SQL>

create oracle sql cursor inside procedure ERROR

I created a procedure for calculating salary of employees by considering shift table data
my shift table is ,
CREATE TABLE SHIFT
(SHIFT_ID NUMBER(4),
SHIFT_DATE DATE,
CUSTOMER_ID NUMBER(4),
SERVICE_ID NUMBER(4),
EMPLOYEE_ID NUMBER(4),
SHIFT_CHARGE FLOAT(10),
PRIMARY KEY(SHIFT_ID)
);
my procedure is,
CREATE OR REPLACE PROCEDURE CAL_SALLARY
AS
CURSOR SHIFT_CURSOR IS SELECT EMPLOYEE_ID,SUM(SHIFT_CHARGE) AS SALARY FROM SHIFT GROUP BY EMPLOYEE_ID;
BEGIN
OPEN SHIFT_CURSOR;
LOOP
FETCH SHIFT_CURSOR INTO SHIFT_REC;
EXIT WHEN SHIFT_CURSOR%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('EMPLOYYE ID : '||SHIFT_REC.EMPLOYEE_ID||' SALARY : '||SHIFT_REC.SALARY);
END LOOP;
CLOSE SHIFT_CURSOR;
END;
/
But when I try to run this procedure in SQL plus, it says compilation fails. I count found what's the issue.
please anyone help me.
You forgot to declare cursor variable (see line #7):
SQL> CREATE OR REPLACE PROCEDURE CAL_SALLARY
2 AS
3 CURSOR SHIFT_CURSOR IS
4 SELECT EMPLOYEE_ID, SUM(SHIFT_CHARGE) AS SALARY
5 FROM SHIFT GROUP BY EMPLOYEE_ID;
6
7 shift_Rec shift_cursor%rowtype;
8 BEGIN
9 OPEN SHIFT_CURSOR;
10 LOOP
11 FETCH SHIFT_CURSOR INTO SHIFT_REC;
12 EXIT WHEN SHIFT_CURSOR%NOTFOUND;
13 DBMS_OUTPUT.PUT_LINE('EMPLOYYE ID : '||SHIFT_REC.EMPLOYEE_ID
14 ||' SALARY : '||SHIFT_REC.SALARY);
15 END LOOP;
16 CLOSE SHIFT_CURSOR;
17 END;
18 /
Procedure created.
SQL>
A simpler option is cursor FOR loop; Oracle does most of dirty job for you (e.g. declaring cursor variable, opening and closing cursor, exiting the loop):
SQL> create or replace procedure cal_sallary as
2 begin
3 for shift_rec in (select employee_id, sum(shift_charge) as salary
4 from shift
5 group by employee_id
6 )
7 loop
8 dbms_output.put_line('EMPLOYYE ID : '||shift_rec.employee_id
9 ||' SALARY : '||shift_rec.salary);
10 end loop;
11 end;
12 /
Procedure created.
SQL>

Oracle Dynamically call cursor

I've two cursors both have almost similar code just a little difference in the group by condition, I want if the id is 1 or 2 e.t.c then it should open cur1 else cur2, basically one cursor at a time. Is there any possibility of achieving this?
if id in (1,2,3,4) then
cursor_value:= 'cur1';
else
cursor_value := 'cur2';
end if;
for i in cursor_value loop
end loop;
You can use an OPEN-FOR statement. Example:
DECLARE
TYPE EmpCurTyp IS REF CURSOR;
v_emp_cursor EmpCurTyp;
emp_record employees%ROWTYPE;
v_stmt_str VARCHAR2(200);
v_e_job employees.job%TYPE;
BEGIN
-- Dynamic SQL statement with placeholder:
v_stmt_str := 'SELECT * FROM employees WHERE job_id = :j';
-- Open cursor & specify bind argument in USING clause:
OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER';
-- Fetch rows from result set one at a time:
LOOP
FETCH v_emp_cursor INTO emp_record;
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
-- Close cursor:
CLOSE v_emp_cursor;
END;
/
One option would be to include both cursor's SELECT statements into cursor FOR loop, along with a condition that chooses which one of them will be used. For example:
SQL> declare
2 id number := &par_id;
3 begin
4 for cur_r in (select * from emp
5 where deptno = 10
6 and id in (1,2,3,4)
7 union all
8 select * from emp
9 where deptno = 20
10 and id not in (1,2,3,4)
11 )
12 loop
13 dbms_output.put_Line(cur_r.deptno ||' '||cur_r.ename);
14 end loop;
15 end;
16 /
Enter value for par_id: 1 --> ID = 1, so use SELECT for DEPTNO = 10
old 2: id number := &par_id;
new 2: id number := 1;
10 CLARK
10 KING
10 MILLER
PL/SQL procedure successfully completed.
SQL> /
Enter value for par_id: 823 --> ID <> 1, so use SELECT for DEPTNO = 20
old 2: id number := &par_id;
new 2: id number := 823;
20 SMITH
20 JONES
20 SCOTT
20 ADAMS
20 FORD
PL/SQL procedure successfully completed.
SQL>
The simple way could be to use if else condition.
DECLARE
type cur REF CURSOR;
c cur;
BEGIN
IF id in (1,2,3,4) THEN
OPEN c FOR 'cursor query 1';
ELSE
OPEN c FOR 'cursor query 2';
END IF ;
END;
Cheers!!

How do I assign number of counted ID's from sub-query to plsql declared variable "v_count"

I'm trying create a pl/sql block that retrieves manager’s information .
List item
The program must prompt the user to enter manager’s number and later displays the total number of employees working under that manager.
SERVER OUTPUT ON
DECLARE
v_last_name s_emp.last_name%TYPE;
v_first_name s_emp.first_name%TYPE;
v_count NUMBER (10);
BEGIN
SELECT DISTINCT last_name,first_name
INTO v_surname,v_name,v_count
FROM s_emp
WHERE ID IN (SELECT manager_id, COUNT(ID)
FROM s_emp
WHERE manager_id = &ID
GROUP BY manager_id);
DBMS_OUTPUT.PUT_LINE('Manager '||v_last_name||'
'||SUBSTR(v_first_name,1,1)||' , has '||v_count||' surbodinates');
END;
I've also tried SQL%ROWCOUNT. And i'm not allowed to use loops and Explicit Cursors yet
This is the error i get when i try to run the code:
PL/SQL: ORA-00913: too many values
Errors you made:
number of columns returned by a select statement in PL/SQL must match number of variables you're putting their values into. You can't select 3 values and put them into 2 variables
where clause: ID can't be in two values returned by a subquery (manager_id, count(id)); must be only one
sql%rowcount won't help here, as its value remains 1 (if select returns a row; if it returns more than a single row, that code will raise too-many-rows; if it returns no rows, it'll raise no-data-found)
An example of how you might have done it (based on Scott's sample schema):
SQL> set verify off
SQL> set serveroutput on
SQL> declare
2 v_ename emp.ename%type;
3 v_count number;
4 begin
5 select ename
6 into v_ename
7 from emp
8 where empno = &&manager_id;
9
10 select count(*)
11 into v_count
12 from emp
13 where mgr = &&manager_id;
14
15 dbms_output.put_line('Manager ' || v_ename || ' has ' || v_count ||
16 ' subordinates');
17 end;
18 /
Enter value for manager_id: 7698
Manager BLAKE has 5 subordinates
PL/SQL procedure successfully completed.
SQL>

Resources