cursor value in other select query - plsql

create or replace PROCEDURE "RESULT" (res1 OUT SYS_REFCURSOR )
IS
Cursor c is Select distinct id,fname,lname,dob,gender,address1 from emp where name like '%B%'
d c%rowtype;
BEGIN
OPEN c;
LOOP
fetch c into d;
exit when c%notfound;
OPEN res1 FOR select e.id from emp e where e.poi_user_id IN (d.id);
End Loop;
END;
Procedure RESULT compiled.
if i run query without procedure i get 5 results but when i am using the above code, it only returns the last result.
SET SERVEROUTPUT ON;
Declare
c SYS_REFCURSOR;
id number;
begin
RESULT(c);
loop
fetch c into id; -- and other columns if needed
exit when c%notfound;
dbms_output.put_line(id);
end loop;
END;
Result 5

You shouldn't make the coding difficult for yourself to understand. You dont need any loop here in this case a simple SELECT with FILTER condition will be enough to suffice your requirement. Hope below query helps. Also its not a good coding practice to user "" for naming convention.
As far as your question is concerned REFCURSOR is not that intelligent to keep all the records for each iteration and print you the collaborated output.
CREATE OR REPLACE PROCEDURE "RESULT"(
res1 OUT SYS_REFCURSOR )
IS
BEGIN
OPEN res1 FOR
SELECT e.id FROM emp e
WHERE EXISTS
( SELECT 1
FROM emp E1
WHERE e1.name LIKE '%B%'
AND e1.poi_user_id = e.id
);
END;

Related

How to use the for loop in fetching Id's from a rows in a table to be used by a procedure in PLSQL?

This is my code below I get this error(Error at line 24/8: ORA-06550: line 20, column 12:PLS-00201: identifier 'A.ID' must be declared) as shown in the image below when I try running the code. Please how can I write the plsql code properly(using for loop) to fetch each row ID and pass them to the procedure?
BEGIN
DECLARE
p_id number(30);
p_status varchar(20);
BEGIN
for c in (
SELECT
a.ID,
a.STATUS
INTO
p_id,
p_status
from USER_COMMISSIONS a,
order_line b where a.order_line_id=b.id and a.status= 'unconfirmed'
)
LOOP
begin
p_id := a.ID;
p_status := a.STATUS;
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
end;
-- update pstk_payload set status = 'done' where id = pyld_id;
dbms_output.put_line(p_id);
-- PSTK_PAYMENT_PACKAGE.add_payment(p_amt, p_user_id, p_reference, p_name, p_narration, p_payment_date, p_net_amt, p_payment_type_id, p_transaction_type_id, p_payment_id, p_status);
END LOOP;
end;
END;
There's nothing to declare, actually - everything you need (at least, in code you posted and that's not commented) is contained in cursor itself.
As William commented, you need to reference columns with the cursor name (not tables that are their source).
Also, no need for any exception handler; cursor certainly won't return no_data_found; if its select doesn't return anything the only "consequence" will be that none of commands within the loop will be executed.
If you're joining tables, then use JOIN; leave where clause for conditions (if any).
Therefore:
begin
for c in (select a.id,
a.status
from user_commissions a join order_line b on a.order_line_id = b.id
where a.status= 'unconfirmed'
)
loop
dbms_output.put_line(c.id ||', '|| c.status);
end loop;
end;

oracle plsql procedure dynamic count of the tables in cursor loop

I want to update all the tables having ABC column.Need to skip the tables which doesn't have data.I am having problem in checking the count of the table in a cursor loop.
PLSQL code
create or replace procedure testp is
CURSOR c_testp
IS
SELECT table_name,
column_name
FROM all_tab_columns
WHERE column_name IN('ABC')
ORDER BY table_name;
c int;
BEGIN
FOR table_rec IN c_testp
LOOP
BEGIN
SELECT COUNT(*)
INTO c
FROM table_rec.table_name;
IF(c>0) THEN
query := 'update '||table_rec.table_name||' set '||table_rec.column_name ||'= xyz';
EXECUTE IMMEDIATE query;
COMMIT;
END IF;
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('data not found');
WHEN OTHERS THEN
dbms_output.put_line('others');
END;
END LOOP;
END;
In your code, use this:
EXECUTE IMMEDIATE 'SELECT count(*) FROM ' || table_rec.table_name INTO c;
instead of this:
SELECT COUNT(*)
INTO c
FROM table_rec.table_name;
However, as mentioned in comments - there is actually no need to perform that condition check, as no update will be performed when table is empty.

How to stop updating null values in oracle

I have a sql procedure which perfectly works. please find it below.
declare
cid number;
cadd number;
ctras number;
pr varchar(2);
vad number;
cursor c1 IS
select ac_tras, cust_id, cust_addr from customer_master;
cursor c2 IS
select pr_adr from customer_address where cust_id = cid and cust_addr = cadd;
BEGIN
open c1;
LOOP
fetch c1 into ctras, cid, cadd;
EXIT WHEN C1%NOTFOUND;
OPEN c2;
LOOP
fetch c2 into pr;
if pr='Y'
THEN EXIT ;
ELSE
UPDATE customer_master
set cust_addr = (select cust_addr from customer_address where pr_adr = 'Y' and cust_id = cid) where ac_tras = ctras;
END IF;
EXIT WHEN C2%NOTFOUND;
END LOOP;
Close C2;
END LOOP;
CLOSE C1;
END;
Everything works fine. The problem is, The update statement updates null if the sub query returns null. How to avoid this.
If the subquery doesn't find a matching row then the master table will be updated with null, because ther eis no filter to stop that. A common way to avoid that is to check that a matching row does exist:
UPDATE customer_master
set cust_addr = (
select cust_addr from customer_address
where pr_adr = 'Y' and cust_id = cid)
where ac_tras = ctras
and exists (
select cust_addr from customer_address
where pr_adr = 'Y' and cust_id = cid)
;
It doesn't really matter which column name you use in the exists clause; some people prefer to use select * or select null but it seems to be a matter of taste really (unless you specify a column you aren't going to be using later and which can't be retrieved from an index you're using anyway, which could force an otherwise unnecessary table row lookup).
You could also do a merge. And has been pointed out several times now, you don't need cursors or any PL/SQL to do this.

Can I pass an explicit cursor to a function/procedure for use in FOR loop?

I have a procedure that performs some calculations on all records returned by a cursor. It looks a bit like this:
PROCEDURE do_calc(id table.id_column%TYPE)
IS
CURSOR c IS
SELECT col1, col2, col3
FROM table
WHERE ...;
BEGIN
FOR r IN c LOOP
-- do some complicated calculations using r.col1, r.col2, r.col3 etc.
END LOOP;
END;
Now I have the case where I need to perform the exact same calculation on a different set of records that come from a different table. However, these have the same "shape" as in the above in example.
Is it possible to write a procedure that looks like this:
PROCEDURE do_calc2(c some_cursor_type)
IS
BEGIN
FOR r IN c LOOP
-- do the calc, knowing we have r.col1, r.col2, r.col3, etc.
END LOOP;
END;
I know about SYS_REFCURSOR, but I was wondering if it was possible to use the much more convenient FOR ... LOOP syntax and implicit record type.
Create a package.
Declare your cursor as package variable.
Use %rowtype to set function parameter type.
create or replace package test is
cursor c is select 1 as one, 2 as two from dual;
procedure test1;
function test2(test_record c%ROWTYPE) return number;
end test;
create or replace package body test is
procedure test1 is
begin
for r in c loop
dbms_output.put_line(test2(r));
end loop;
end;
function test2(test_record c%ROWTYPE) return number is
l_summ number;
begin
l_summ := test_record.one + test_record.two;
return l_summ;
end;
end test;
I had a similar problem, where I had two cursors that needed to be processed the same way, so this is how I figured it out.
DECLARE
--Define our own rowType
TYPE employeeRowType IS RECORD (
f_name VARCHAR2(30),
l_name VARCHAR2(30));
--Define our ref cursor type
--If we didn't need our own rowType, we could have this: RETURN employees%ROWTYPE
TYPE empcurtyp IS REF CURSOR RETURN employeeRowType;
--Processes the cursors
PROCEDURE process_emp_cv (emp_cv IN empcurtyp) IS
person employeeRowType;
BEGIN
LOOP
FETCH emp_cv INTO person;
EXIT WHEN emp_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Name = ' || person.f_name ||
' ' || person.l_name);
END LOOP;
END;
--Defines the cursors
PROCEDURE mainProcedure IS
emp empcurtyp;
BEGIN
OPEN emp FOR SELECT first_name, last_name FROM employees WHERE salary > 50000;
process_emp_cv(emp);
CLOSE emp;
OPEN emp FOR SELECT first_name, last_name FROM kuren WHERE first_name LIKE 'J%';
process_emp_cv(emp);
CLOSE emp;
END;
BEGIN
mainProcedure;
END;
/
You can also use this if you want to bulk collect your cursors. You just need to change your helper procedure process_emp_cv; the rest can stay the same.
Using BULK COLLECT
--Processes the cursors
PROCEDURE process_emp_cv (emp_cv IN empcurtyp) IS
TYPE t_employeeRowTable IS TABLE OF employeeRowType;
employeeTable t_employeeRowTable;
BEGIN
LOOP
FETCH emp_cv BULK COLLECT INTO employeeTable LIMIT 50;
FOR indx IN 1 .. employeeTable.Count
LOOP
DBMS_OUTPUT.PUT_LINE('Name = ' || employeeTable(indx).f_name ||
' ' || employeeTable(indx).l_name);
END LOOP;
EXIT WHEN emp_cv%NOTFOUND;
END LOOP;
END;
Try this one, Usong ref cursor.
declare
type c is ref cursor;
c2 c;
type rec is record(
id number,
name varchar(20)
);
r rec;
procedure p1(c1 in out c,r1 in out rec)is begin
loop
fetch c1 into r1;
exit when c1%notfound;
dbms_output.put_line(r1.id || ' ' ||r1.name);
end loop;
end;
begin
open c2 for select id, name from student;
p1(c2,r);
end;
Yes you can use Cursor explicitly into procedure and function,for that cursor need to declare into package as variable

plsql cursor iterating problem

i use oracle demo schema scott to do some plsql test ( the data in that schema are never changed ). i wrote the following program to get the employee number of each department. the problem is, there is just 4 departments but my program output 5 row. i can't find out the reason, anyone can help? great thanks.
declare
cursor employees(department_id number) is
select count(*) howmany
from scott.emp
where deptno=department_id;
employees_per_dept employees%rowtype;
cursor departments is
select *
from scott.dept;
a_department departments%rowtype;
begin
dbms_output.put_line('-----------------------------------');
open departments;
loop
exit when departments%notfound;
fetch departments into a_department;
open employees(a_department.deptno);
fetch employees into employees_per_dept;
dbms_output.put_line(employees_per_dept.howmany);
close employees;
end loop;
close departments;
dbms_output.put_line('-----------------------------------');
end;
If you output the deptno in the dbms_output you'll see the reason.
You need to switch these two lines:
fetch departments into a_department;
exit when departments%notfound;
%NOTFOUND is meaningless before the initial FETCH; your code was counting the emps in the last dept twice.
declare
cursor cl(ccode varchar2) is
Select * from employees where department_id=ccode;
z cl%rowtype;
cnt number:=0;
begin
Open cl('90');
fetch cl into Z;
while (cl%found) loop
dbms_output.put_line ( 'nsme is ' || z.last_name);
fetch cl into Z;
cnt := cnt +1;
end loop;
dbms_output.put_line (cnt);
close cl;
end;

Resources