Why we need Cursors? What are the advantages of using different
types of Cursors?
- Cursor-Is a memory object that will be stored inside your RAM.
- Whenever Cursor executed inside the PL/SQL block memory allocated/deallocated automatically based on the type of cursor.
- Cursor is used to access the result set present in the memory. This result set contains the records returned on execution of a query.
- The data that is stored in the cursor is called as Active Data Set so can also defined as a pointer to an Active Result set.
- The TOO_MANY_ROWS Exception (ORA-01422) occurs when a SELECT INTO statement returns more than one row. Cursor helps us to avoid this error.
- Opening the cursor executes the query and identifies the result set, which consists of all rows that meet the query search criteria. For cursors declared using the FOR UPDATE clause, the OPEN statement also locks those rows.
To know how Cursor works internally- Click here.
Types of Cursors-
- Implicit Cursor
- Explicit Cursor (Simple, Parameterized and Ref Cursor)
- Package Cursor
Implicit Cursor (Predefined)-
Opened - Fetched and closed automatically.
Implicit Cursor is created whenever
we execute SQL within a PL/SQL Block and we don’t have any control over it as
it is controlled by Oracle itself and also it doesn’t have any name.
Attributes of Implicit
Cursor-
- SQL%OPEN- Will always return FALSE
- SQL%FOUND- Will return TRUE whenever previous SQL statement is executed successfully.
- SQL%NOTFOUND- Opposite to SQL%FOUND- Will return FALSE if previous SQL statement executed successfully.
- SQL%ROWCOUNT- Will return no of rows affected by the previously executed statement.
Begin
Delete from EMP_B where Dept_No=10;
If (SQL%FOUND) Then
DBMS_OUTPUT.PUT_LINE (‘Number of
records Deleted- ‘|| SQL%ROWCOUNT);
Else
DBMS_OUTPUT.PUT_LINE (‘No records to
be deleted !!!’);
End If;
END;
SQL> SELECT * From EMP_B Order By DEPT_NO; ID NAME DEPT_NO ---------- ---------- ---------- 101 Ravi 10 110 Aman 10 104 Manu 10 107 Akhil 20 111 Nik 20 109 Shiva 20 106 Subho 20 113 Shaan 30 102 Rajan 30 105 Gaurav 30 103 Pritesh 30 108 Neeraj 40 112 Sri 40 13 rows selected.
SQL> Begin
2 Delete from EMP_B where Dept_No=10; 3 If (SQL%FOUND) Then 4 DBMS_OUTPUT.PUT_LINE ('Number of records Deleted- '|| SQL%ROWCOUNT); 5 Else 6 DBMS_OUTPUT.PUT_LINE ('No records to be deleted !!!'); 7 End If; 8 End; 9 / Number of records Deleted- 3 PL/SQL procedure successfully completed. SQL> SELECT * From EMP_B Order By DEPT_NO; ID NAME DEPT_NO ---------- ---------- ---------- 107 Akhil 20 111 Nik 20 109 Shiva 20 106 Subho 20 113 Shaan 30 105 Gaurav 30 103 Pritesh 30 102 Rajan 30 108 Neeraj 40 112 Sri 40 10 rows selected.
Explicit Cursor (User
Defined)-
Created by users for processing multiple
records to avoid too many rows error. Explicit cursor will have a name
associated with it. An explicit cursor is defined in the
declaration section of the PL/SQL Block
- DECLARE the cursor in the declaration section.
- OPEN the cursor in the Execution Section.
- FETCH the data from cursor into PL/SQL variables or records in the Execution Section.
- CLOSE the cursor in the Execution Section before you end the PL/SQL Block.
- Cur_Name%Open- Will return True if cursor is Open Else False.
- Cur_Name%Found- Will return TRUE whenever previous SQL statement is executed successfully.
- Cur_Name%NotFound- Opposite to SQL%FOUND- Will return FALSE if previous SQL statement executed successfully.
- Cur_ Name%ROWCOUNT- Will return no of rows affected by the previously executed statement.
- Simple Cursor
- Parameterized Cursor
- Ref Cursor
Query remain constant only where condition changes based on the requirement. In the below example value is hard coded.
Declare
Cursor Cur_Emp Is
Select Name,Salary from EMP_TEST
Where DeptNO=10;
Cursor Cur_Emp2 Is
Select Name,Salary from EMP_TEST
where DeptNo=20;
Vnm Emp_TEST.NAME%Type;
Vsal Emp_TEST.Salary%Type;
Begin
Open Cur_Emp;
Loop
Fetch
Cur_Emp Into Vnm, Vsal;
Exit
When Cur_Emp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(Vnm
|| '-'|| Vsal);
End
Loop;
DBMS_OUTPUT.PUT_LINE('Cursor1
Row Count : ' ||Cur_Emp%ROWCOUNT);
Close
Cur_Emp;
Open
Cur_Emp2;
Loop
Fetch
Cur_Emp2 Into Vnm, Vsal;
Exit
When Cur_Emp2%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(Vnm
|| '-'|| Vsal);
End
Loop;
DBMS_OUTPUT.PUT_LINE('Cursor2
Row Count : ' ||Cur_Emp2%ROWCOUNT);
Close
Cur_Emp2;
End;
Let's execute above PL/SQL Block :
SQL> SELECT * From EMP_TEST; ID NAME SALARY STATUS DEPTNO MANAGERID ---------- ---------- ---------- ---------- ---------- ---------- 102 Ankit 8000 Active 20 101 104 Nikhil 69000 Active 10 101 105 Rajan 18000 Active 20 112 107 Karan 101000 Active 20 112 110 Sajal 88000 Active 10 101 SQL> Declare 2 Cursor Cur_Emp Is 3 Select Name,Salary from EMP_TEST Where DeptNO=10; 4 Cursor Cur_Emp2 Is 5 Select Name,Salary from EMP_TEST where DeptNo=20; 6 Vnm Emp_TEST.NAME%Type; 7 Vsal Emp_TEST.Salary%Type; 8 Begin 9 Open Cur_Emp; 10 Loop 11 Fetch Cur_Emp Into Vnm, Vsal; 12 Exit When Cur_Emp%NOTFOUND; 13 DBMS_OUTPUT.PUT_LINE(Vnm || '-'|| Vsal); 14 End Loop; 15 DBMS_OUTPUT.PUT_LINE('Cursor1 Row Count : ' ||Cur_Emp%ROWCOUNT); 16 Close Cur_Emp; 17 18 Open Cur_Emp2; 19 Loop 20 Fetch Cur_Emp2 Into Vnm, Vsal; 21 Exit When Cur_Emp2%NOTFOUND; 22 DBMS_OUTPUT.PUT_LINE(Vnm || '-'|| Vsal); 23 End Loop; 24 DBMS_OUTPUT.PUT_LINE('Cursor2 Row Count : ' ||Cur_Emp2%ROWCOUNT); 25 Close Cur_Emp2; 26 End; 27 / Nikhil-69000 Sajal-88000 Cursor1 Row Count : 2 Ankit-8000 Rajan-18000 Karan-101000 Cursor2 Row Count : 3 PL/SQL procedure successfully completed.
Parameterized Cursor- (Reusability and Maintainability)
Limitation of above cursor is resolved using
Parameterized cursor. It has ability to manipulate
conditions at run time.
Declare
Cursor C1(V_deptno NUMBER) IS
Select Name, Salary From Emp_Test
Where Deptno=V_deptno;
Begin
For rec in C1(10)
Loop
DBMS_OUTPUT.PUT_LINE('Employee – '
|| rec.Name || ' Has Salary – ' || rec.Salary);
End Loop;
End;
SQL> SELECT * From EMP_TEST; ID NAME SALARY STATUS DEPTNO MANAGERID ---------- ---------- ---------- ---------- ---------- ---------- 102 Ankit 8000 Active 20 101 104 Nikhil 69000 Active 10 101 105 Rajan 18000 Active 20 112 107 Karan 101000 Active 20 112 110 Sajal 88000 Active 10 101 SQL> Declare 2 Cursor C1(V_deptno NUMBER) IS 3 Select Name, Salary From Emp_Test Where Deptno=V_deptno; 4 Begin 5 For rec in C1(10) 6 Loop 7 DBMS_OUTPUT.PUT_LINE('Employee - ' || rec.Name || ' Has Salary - ' || rec.Salary); 8 End Loop; 9 End; 10 / Employee - Nikhil Has Salary - 69000 Employee - Sajal Has Salary - 88000 PL/SQL procedure successfully completed.
Example
– Print Department Wise Employee names and their Salary.
Declare
Cursor C_Dept IS
Select Distinct DeptNo From
EMP_TEST Order By DeptNO;
Cursor C1(V_Deptno NUMBER) IS
Select Name, Salary From Emp_Test
Where Deptno=V_Deptno;
Begin
For rec IN C_Dept Loop
DBMS_OUTPUT.PUT_LINE('DeptNo
– ' || rec.Deptno);
For rec1 IN C1(rec.Deptno) Loop
DBMS_OUTPUT.PUT_LINE('Employee
' || rec1.Name || ' Has Salary – ' || rec1.Salary);
End Loop;
End Loop;
End;
SQL> SELECT * From EMP_TEST; ID NAME SALARY STATUS DEPTNO MANAGERID ---------- ---------- ---------- ---------- ---------- ---------- 102 Ankit 8000 Active 20 101 104 Nikhil 69000 Active 10 101 105 Rajan 18000 Active 20 112 107 Karan 101000 Active 20 112 110 Sajal 88000 Active 10 101 SQL> Declare 2 Cursor C_Dept IS 3 Select Distinct DeptNo From EMP_TEST Order By DeptNO; 4 Cursor C1(V_Deptno NUMBER) IS 5 Select Name, Salary From Emp_Test Where Deptno=V_Deptno; 6 Begin 7 For rec IN C_Dept Loop 8 DBMS_OUTPUT.PUT_LINE('DeptNo - ' || rec.Deptno); 9 For rec1 IN C1(rec.Deptno) Loop 10 DBMS_OUTPUT.PUT_LINE('Employee ' || rec1.Name || ' Has Salary - ' || rec1.Salary); 11 End Loop; 12 End Loop; 13 End; 14 / DeptNo - 10 Employee Nikhil Has Salary - 69000 Employee Sajal Has Salary - 88000 DeptNo - 20 Employee Ankit Has Salary - 8000 Employee Rajan Has Salary - 18000 Employee Karan Has Salary - 101000 PL/SQL procedure successfully completed.
Ref Cursor- (Type of Explicit Cursor)
Reference cursor works on the concept of Dynamic SQL.
It can store different result set at different point of time.
Ref cursor is user defined data type- By using this we can create multiple number
of variable(s) - With a single variable we can retrieve different result set
and very much useful to return multiple records from SP or Function.
Example-
Declare
Type test1 is Ref Cursor;
Var test1;
Vrec EMP_TEST%ROWTYPE;
Vrec2 DEPT_TEST%ROWTYPE;
Begin
DBMS_OUTPUT.PUT_LINE('----------------EMPLOYEES-----------------------');
Open Var for 'Select * from
EMP_TEST';
Loop
Fetch Var into Vrec;
Exit When Var%NotFound;
DBMS_OUTPUT.PUT_LINE(Vrec.Name);
End Loop;
DBMS_OUTPUT.PUT_LINE('----------------DEPARTMENTS-----------------------');
Open Var for 'Select * from
DEPT_TEST';
Loop
Fetch Var into Vrec2;
Exit When Var%NotFound;
DBMS_OUTPUT.PUT_LINE(Vrec2.DEPARTMENT);
End Loop;
Close Var;
End;
SQL> SELECT * From EMP_TEST; ID NAME SALARY STATUS DEPTNO MANAGERID ---------- ---------- ---------- ---------- ---------- ---------- 102 Ankit 8000 Active 20 101 104 Nikhil 69000 Active 10 101 105 Rajan 18000 Active 20 112 107 Karan 101000 Active 20 112 110 Sajal 88000 Active 10 101 SQL> Declare 2 Type test1 is Ref Cursor; 3 Var test1; 4 Vrec EMP_TEST%ROWTYPE; 5 Vrec2 DEPT_TEST%ROWTYPE; 6 Begin 7 DBMS_OUTPUT.PUT_LINE('----------------EMPLOYEES-----------------------'); 8 Open Var for 'Select * from EMP_TEST'; 9 Loop 10 Fetch Var into Vrec; 11 Exit When Var%NotFound; 12 DBMS_OUTPUT.PUT_LINE(Vrec.Name); 13 End Loop; 14 DBMS_OUTPUT.PUT_LINE('----------------DEPARTMENTS-----------------------'); 15 Open Var for 'Select * from DEPT_TEST'; 16 Loop 17 Fetch Var into Vrec2; 18 Exit When Var%NotFound; 19 DBMS_OUTPUT.PUT_LINE(Vrec2.DEPARTMENT); 20 End Loop; 21 Close Var; 22 End; 23 / ----------------EMPLOYEES----------------------- Ankit Nikhil Rajan Karan Sajal ----------------DEPARTMENTS----------------------- Security ENU Insurance Retail BFSI CISCO Accounts Finance PL/SQL procedure successfully completed.
Ref cursors are of two types -
1) Strong ref cursor: Which
returns value.
When
return type included then it is called strong or static structure type. Static
ref cursor supports different type of select statement but all of same
structure, but not necessary that the table should be same.
2) Weak ref cursor: Which
doesn't return value.
Weak
ref cursor allows us to any type of select statement irrespective of data
structure.
Strong cursor example
Declare
Type Emprefcur is Ref Cursor Return
EMP_TEST%rowtype;
ec Emprefcur;
v_ec ec%rowtype;
Begin
Open ec For Select * From EMP_TEST;
Loop
Fetch ec into v_ec;
Exit when ec%notfound;
DBMS_OUTPUT.PUT_LINE(v_Ec.ID||' -
'||v_ec.Name);
End loop;
Close ec;
DBMS_OUTPUT.PUT_LINE('-------------------------------------------------------------------------');
Open ec For Select * From EMP_TEST;
Loop
Fetch ec into v_ec;
Exit when ec%notfound;
DBMS_OUTPUT.PUT_LINE(v_Ec.ID||' -
'||v_ec.Name);
End loop;
Close ec;
End;
Weak cursor
example
Declare
Type refcur is Ref cursor;
xc refcur;
v_Ec EMP_TEST%rowtype;
v_dc DEPT_TEST%rowtype;
Begin
Open xc For Select * From
EMP_TEST;
Loop
Fetch xc into v_ec;
Exit when xc%notfound;
DBMS_OUTPUT.PUT_LINE(v_ec.Name ||'-'||v_Ec.ID);
End loop;
Close xc;
DBMS_OUTPUT.PUT_LINE('--------------------------------------');
Open xc For Select * From DEPT_TEST;
Loop
Fetch xc into v_dc;
Exit when xc%notfound;
DBMS_OUTPUT.PUT_LINE(v_dc.Deptno||'-'||v_dc.Department);
End loop;
Close xc;
End;
**Package Cursor**
A Package that returns a
Cursor type is a package cursor.
In a package cursor, the SQL statement for the cursor is
attached dynamically at runtime from calling procedures.
Example-
Create or
Replace Package Pkg_Util is
Cursor c_emp is Select * from EMP_TEST;
r_emp c_emp%ROWTYPE;
End;
Cursor c_emp is Select * from EMP_TEST;
r_emp c_emp%ROWTYPE;
End;
/*Another package using this package*/
Create or Replace Package Body pkg_aDifferentUtil is
Procedure p_printEmps is
Begin
Open pkg_Util.c_emp;
Loop
Fetch pkg_Util.c_emp into pkg_Util.r_emp;
Exit when pkg_Util.c_emp%NOTFOUND;
DBMS_OUTPUT.Put_Line(pkg_Util.r_emp.NAME);
End loop;
Close pkg_Util.c_emp;
End;
End;
Create or Replace Package Body pkg_aDifferentUtil is
Procedure p_printEmps is
Begin
Open pkg_Util.c_emp;
Loop
Fetch pkg_Util.c_emp into pkg_Util.r_emp;
Exit when pkg_Util.c_emp%NOTFOUND;
DBMS_OUTPUT.Put_Line(pkg_Util.r_emp.NAME);
End loop;
Close pkg_Util.c_emp;
End;
End;
You can use any of the above Cursor types as per your requirement.
Get involved
and leave your Comments in the Box Below. The more people get involved, the
more we all benefit. So, leave your thoughts before you leave
the page.
Nice Explanation.thank you sir
ReplyDeleteGood work this is search a nice article.
ReplyDeleteOracle Fusion HCM Training
This article is a great article that I have seen so far in my SQL career, it helps a lot in cursor programming.
ReplyDeletewebsite development company in Surat Gujarat
This comment has been removed by the author.
ReplyDelete