CURSORS
Cursor is a pointer to memory location which is called as context area which contains the information necessary for processing, including the number of rows processed by the statement, a pointer to the parsed representation of the statement, and the active set which is the set of rows returned by the query.
Cursor contains two parts:
- Header
- Body
Header includes cursor name, any parameters and the type of data being loaded.
Body includes the select statement.
Ex:
Cursor c(dno in number) return dept%rowtype is select *from dept;
In the above
Header – cursor c(dno in number) return dept%rowtype
Body – select *from dept
Cursor Types:
- Implicit Cursor(SQL)
- Explicit Cursor
a) Parameterized Cursors
b) REF cursors
Cursor Stages:
- Open
- Fetch
- Close
Cursor Attributes:
- %found
- %notfound
- %rowcount
- %isopen
- %bulk_rowcount
- %bulk_exceptions
Cursor Declaration:
Syntax:
Cursor <cursor_name> is select statement;
EX:
Cursor c is select * from dept;
Cursor Loops:
- Simple Loop
- While Loop
- For Loop
1.Simple Loop:
Syntax:
Loop
Fetch <cursor_name> into <record_variable>;
Exit when <cursor_name> % notfound;
<statements>;
End loop;
Ex:
DECLARE
cursor c is select * from student;
v_stud student%rowtype;
BEGIN
Open c;
Loop
fetch c into v_stud;
exit when c%notfound;
dbms_output.put_line('Name = ' || v_stud.name);
end loop;
close c;
END;
Output:
Name = saketh
Name = srinu
Name = satish
Name = sudha
2.While Loop:
Syntax:
While <cursor_name> % found
loop
Fetch <cursor_name> into <record_variable>;
<statements>;
End loop;
Ex:
DECLARE
cursor c is select * from student;
v_stud student%rowtype;
BEGIN
open c;
fetch c into v_stud;
while c%found loop
fetch c into v_stud;
dbms_output.put_line('Name = ' || v_stud.name);
end loop;
close c;
END;
Output:
Name = saketh
Name = srinu
Name = satish
Name = sudha
3.For Loop:
Syntax:
for <record_variable> in <cursor_name>
loop
<statements>;
End loop;
Ex:
declare
cursor c is select * from student;
begin
for v_stud in c
loop
dbms_output.put_line('Name = ' || v_stud.name);
end loop;
end;
Output:
Name = saketh
Name = srinu
Name = satish
Name = sudha
Implicit Cursors:
============
An Example of PL/SQL Attribute
DECLARE
rows_deleted NUMBER;
BEGIN
DELETE * FROM emp;
rows_deleted := SQL%ROWCOUNT;
END;
The implicit cursor has the following drawbacks:
Explicit Cursors:
============
Explicit cursors are programmer defined cursors for gaining more control over the context area. An explicit cursor should be defined in the declaration section of the PL/SQL Block. It is created on a SELECT Statement which returns more than one row.
The syntax for creating an explicit cursor is :
CURSOR cursor_name IS select_statement;
Working with an explicit cursor involves four steps:
============
- Implicit cursors are automatically created by Oracle whenever an SQL statement is executed, when there is no explicit cursor for the statement. Programmers cannot control the implicit cursors and the information in it.
- Whenever a DML statement (INSERT, UPDATE and DELETE) is issued, an implicit cursor is associated with this statement. For INSERT operations, the cursor holds the data that needs to be inserted. For UPDATE and DELETE operations, the cursor identifies the rows that would be affected
- In PL/SQL, you can refer to the most recent implicit cursor as the SQL cursor, which always has the attributes like %FOUND, %ISOPEN, %NOTFOUND, and %ROWCOUNT. The SQL cursor has additional attributes, %BULK_ROWCOUNT and %BULK_EXCEPTIONS, designed for use with the FORALL statement. The following table provides the description of the most used attributes:
The following program would update the table and increase salary of each customer by 500 and use the SQL%ROWCOUNT attribute to determine the number of rows affected.
DECLARE
total_rows number(2);
BEGIN
UPDATE customers
SET salary = salary + 500;
IF sql%notfound THEN
dbms_output.put_line('no customers selected');
ELSIF sql%found THEN
total_rows := sql%rowcount;
dbms_output.put_line( total_rows || ' customers selected ');
END IF;
END;
/
PL/SQL provides some attributes, which allow you to evaluate what happened when the implicit cursor was last used. You can use these attributes in PL/SQL statements like functions but you cannot use then within SQL statements.
%ROWCOUNT | The number of rows processed by a SQL statement. |
%FOUND | TRUE if at least one row was processed. |
%NOTFOUND | TRUE if no rows were processed. |
%ISOPEN | TRUE if cursor is open or FALSE if cursor has not been opened or has been closed. Only used with explicit cursors. |
An Example of PL/SQL Attribute
DECLARE
rows_deleted NUMBER;
BEGIN
DELETE * FROM emp;
rows_deleted := SQL%ROWCOUNT;
END;
The implicit cursor has the following drawbacks:
- It is less efficient than an explicit cursor.
- It is more vulnerable to data errors.
- It gives you less programmatic control.
============
Explicit cursors are programmer defined cursors for gaining more control over the context area. An explicit cursor should be defined in the declaration section of the PL/SQL Block. It is created on a SELECT Statement which returns more than one row.
The syntax for creating an explicit cursor is :
CURSOR cursor_name IS select_statement;
Working with an explicit cursor involves four steps:
- Declaring the cursor for initializing in the memory
- Opening the cursor for allocating memory
- Fetching the cursor for retrieving data
- Closing the cursor to release allocated memory
Declaring the Cursor:
Declaring the cursor defines the cursor with a name and the associated SELECT statement.
Ex: CURSOR c_customers IS
SELECT id, name, address FROM customers;
Opening the Cursor:
Opening the cursor allocates memory for the cursor and makes it ready for fetching the rows returned by the SQL statement into it.
Ex: OPEN c_customers;
Fetching the Cursor:
Fetching the cursor involves accessing one row at a time.
For example we will fetch rows from the above-opened cursor as follows:
Ex:FETCH c_customers INTO c_id, c_name, c_addr;
Closing the Cursor:
Closing the cursor means releasing the allocated memory.
For example, we will close above-opened cursor as follows:
Ex:CLOSE c_customers;
Example:
Following is a complete example to illustrate the concepts of explicit cursors:
DECLARE
c_id customers.id%type;
c_name customers.name%type;
c_addr customers.address%type;
CURSOR c_customers is
SELECT id, name, address FROM customers;
BEGIN
OPEN c_customers;
LOOP
FETCH c_customers into c_id, c_name, c_addr;
EXIT WHEN c_customers%notfound;
dbms_output.put_line(c_id || ' ' || c_name || ' ' || c_addr);
END LOOP;
CLOSE c_customers;
END;
/
Parameterized cursors:
- This was used when you are going to use the cursor in more than one place with different values for the same where clause.
- Cursor parameters must be in mode.
- Cursor parameters may have default values.
- The scope of cursor parameter is within the select statement.
Ex:
DECLARE
cursor c(dno in number) is select * from dept where deptno = dno;
v_dept dept%rowtype;
BEGIN
open c(20);
loop
fetch c into v_dept;
exit when c%notfound;
dbms_output.put_line('Dname = ' || v_dept.dname || ' Loc = ' || v_dept.loc);
end loop;
close c;
END;
/
Output:
Dname = RESEARCH Loc = DALLAS
Reference Cursors:
This is unconstrained cursor which will return different types depends upon the user input
- Ref Cursors cannot be closed implicitly
- Ref Cursors with return type is called strong cursor
- Ref Cursors with out return type is called weak cursor
- you can declare Ref cursor type in package spec as well as body
- you can declare ref cursor types in local subprograms or anonymous blocks.
Ex:
CREATE OR REPLACE PROCEDURE REF_CURSOR(
TABLE_NAME IN VARCHAR)
IS
type t
IS
ref
CURSOR;
c t;
v_dept dept%rowtype;
type r
IS
record
(
ename emp.ename%type,
job emp.job%type,
sal emp.sal%type);
v_emp r;
v_stud student.name%type;
BEGIN
IF table_name = 'DEPT' THEN
OPEN c FOR SELECT * FROM dept;
elsif table_name = 'EMP' THEN
OPEN c FOR SELECT ename,job,sal FROM emp;
elsif table_name = 'STUDENT' THEN
OPEN c FOR SELECT name FROM student;
END IF;
LOOP
IF table_name = 'DEPT' THEN
FETCH c INTO v_dept;
EXIT
WHEN c%notfound;
dbms_output.put_line('Deptno = ' || v_dept.deptno || ' Dname = ' || v_dept.dname || ' Loc = ' || v_dept.loc);
elsif table_name = 'EMP' THEN
FETCH c INTO v_emp;
EXIT
WHEN c%notfound;
dbms_output.put_line('Ename = ' || v_emp.ename || ' Job = ' || v_emp.job || ' Sal = ' || v_emp.sal);
elsif table_name = 'STUDENT' THEN
FETCH c INTO v_stud;
EXIT
WHEN c%notfound;
dbms_output.put_line('Name = ' || v_stud);
END IF;
END LOOP;
CLOSE c;
END;
Output:
SQL> EXEC ref_cursor('DEPT')
Deptno = 10 Dname = ACCOUNTING Loc = NEW YORK
Deptno = 20 Dname = RESEARCH Loc = DALLAS
Deptno = 30 Dname = SALES Loc = CHICAGO
Deptno = 40 Dname = OPERATIONS Loc = BOSTON
SQL> EXEC ref_cursor('EMP')
Ename = SMITH Job = CLERK Sal = 800
Ename = ALLEN Job = SALESMAN Sal = 1600
Ename = WARD Job = SALESMAN Sal = 1250
Ename = JONES Job = MANAGER Sal = 2975
Ename = MARTIN Job = SALESMAN Sal = 1250
Ename = BLAKE Job = MANAGER Sal = 2850
Ename = CLARK Job = MANAGER Sal = 2450
Ename = SCOTT Job = ANALYST Sal = 3000
Ename = KING Job = PRESIDENT Sal = 5000
Ename = TURNER Job = SALESMAN Sal = 1500
Ename = ADAMS Job = CLERK Sal = 1100
Ename = JAMES Job = CLERK Sal = 950
Ename = FORD Job = ANALYST Sal = 3000
Ename = MILLER Job = CLERK Sal = 1300
SQL> EXEC ref_cursor('STUDENT')
Name = saketh
Name = srinu
Name = satish
Name = sudha
Nested Cursors:
- Oracle opens the nested cursor defined by a cursor expression implicitly as soon as it fetches the data containing the cursor expression from the parent or outer cursor
- Nested cursor closes if you closed explicitly
- Nested cursor closes whenever the outer or parent cursor is executed again or closed or cancelled
- Nested cursor closes whenever an exception is raised while fetching data from a parent cursor
Ex:
DECLARE
cursor c is select ename,cursor(select dname from dept d where e.empno = d.deptno) from emp e;
type t is ref cursor;
c1 t;
c2 t;
v1 emp.ename%type;
v2 dept.dname%type;
BEGIN
open c;
loop
fetch c1 into v1;
exit when c1%notfound;
fetch c2 into v2;
exit when c2%notfound;
dbms_output.put_line('Ename = ' || v1 || ' Dname = ' || v2);
end loop;
end loop;
close c;
END;
CURSOR CLAUSES:
1
Return
2
For
update
3
Where
current of
4
Bulk
collect
RETURN:
Cursor c return dept%rowtype is
select *from dept;
Or
Cursor c1 is select *from dept;
Cursor c return c1%rowtype is select *from dept;
Or
Type t is record(deptno
dept.deptno%type, dname dept.dname%type);
Cursor c return t is select deptno,
dname from dept;
FOR UPDATE AND WHERE CURRENT OF:
Normally, a select operation will not take any locks on
the rows being accessed. This will allow other sessions connected to the
database to change the data being selected. The result set is still consistent.
At open time, when the active set is determined, oracle takes a snapshot of the
table. Any changes that have been committed prior to this point are reflected
in the active set. Any changes made after this point, even if they are
committed, are not reflected unless the cursor is reopened, which will evaluate
the active set again.
However, if the FOR UPDATE
clause is present, exclusive row locks are taken on the rows in the active set
before the open returns. These locks prevent other sessions from changing the
rows in the active set until the transaction is committed or rolled back. If
another session already has locks on the rows in the active set, then SELECT … FOR UPDATE operation will wait for these locks
to be released by the other session. There is no time-out for this waiting
period. The SELECT…FOR
UPDATE will hang until
the other session releases the lock. To handle this situation, the NO WAIT clause is available.
Syntax:
Select …from … for update of
column_name [wait n];
If the cursor is declared with the FOR UPDATE clause, the WHERE CURRENT OF clause can be used in an update or
delete statement.
Syntax:
Where current of cursor;
Ex:
DECLARE
cursor c is select * from dept for update of dname;
BEGIN
for v in c loop
update dept set dname = 'aa' where
current of c;
commit;
end loop;
END;
BULK COLLECT:
1
This
is used for array fetches
2
With
this you can retrieve multiple rows of data with a single round trip.
3
This
reduces the number of context switches between the pl/sql and sql engines.
4
Reduces
the overhead of retrieving data.
5
You
can use bulk collect in both dynamic and static sql.
6
You
can use bulk collect in select, fetch into and returning into clauses.
7 SQL
engine automatically initializes and extends the collections you reference in
the bulk collect clause.
8 Bulk
collect operation empties the collection referenced in the into clause before
executing the query.
9
You
can use the limit clause of bulk collect to restrict the no of rows retrieved.
10 You can fetch into multiple
collections with one column each.
11 Using the returning clause we can
return data to the another collection.
BULK COLLECT IN FETCH
Ex:
DECLARE
Type t is table of dept%rowtype;
nt t;
Cursor c is select *from dept;
BEGIN
Open c;
Fetch c bulk collect into nt;
Close c;
For i in nt.first..nt.last loop
dbms_output.put_line('Dname = ' ||
nt(i).dname || ' Loc = ' ||
nt(i).loc);
end loop;
END;
Output:
Dname = ACCOUNTING Loc = NEW YORK
Dname = RESEARCH Loc = DALLAS
Dname = SALES Loc = CHICAGO
Dname = OPERATIONS Loc = BOSTON
BULK COLLECT IN SELECT
Ex:
DECLARE
Type t is table of dept%rowtype;
Nt t;
BEGIN
Select * bulk collect into nt from dept;
for i in nt.first..nt.last loop
dbms_output.put_line('Dname = ' ||
nt(i).dname || ' Loc = ' ||
nt(i).loc);
end loop;
END;
Output:
Dname
= ACCOUNTING Loc = NEW YORK
Dname
= RESEARCH Loc = DALLAS
Dname
= SALES Loc = CHICAGO
Dname
= OPERATIONS Loc = BOSTON
LIMIT IN BULK COLLECT
You can use this to limit the number of rows to be
fetched.
Ex:
DECLARE
Type t is table of dept%rowtype;
nt t;
Cursor c is select *from dept;
BEGIN
Open c;
Fetch c bulk collect into nt limit 2;
Close c;
For i in nt.first..nt.last loop
dbms_output.put_line('Dname = ' ||
nt(i).dname || ' Loc = ' || nt(i).loc);
end loop;
END;
Output:
Dname = ACCOUNTING Loc = NEW YORK
Dname = RESEARCH Loc = DALLAS
MULTIPLE FETCHES IN
INTO CLAUSE
Ex1:
DECLARE
Type t is table of dept.dname%type;
nt t;
Type t1 is table of dept.loc%type;
nt1 t;
Cursor c is select dname,loc from
dept;
BEGIN
Open c;
Fetch c bulk collect into nt,nt1;
Close c;
For i in nt.first..nt.last
loop
dbms_output.put_line('Dname =
' || nt(i));
end loop;
For i in nt1.first..nt1.last loop
dbms_output.put_line('Loc = '
|| nt1(i));
end loop;
END;
Output:
Dname = ACCOUNTING
Dname = RESEARCH
Dname = SALES
Dname = OPERATIONS
Loc = NEW YORK
Loc = DALLAS
Loc = CHICAGO
Loc = BOSTON
Ex2:
DECLARE
type t is table of
dept.dname%type;
type t1 is table of dept.loc%type;
nt t;
nt1 t1;
BEGIN
Select dname,loc bulk collect into nt,nt1 from dept;
for i in nt.first..nt.last loop
dbms_output.put_line('Dname = ' ||
nt(i));
end loop;
for i in nt1.first..nt1.last loop
dbms_output.put_line('Loc = ' ||
nt1(i));
end loop;
END;
Output:
Dname = ACCOUNTING
Dname = RESEARCH
Dname = SALES
Dname = OPERATIONS
Loc = NEW YORK
Loc = DALLAS
Loc = CHICAGO
Loc = BOSTON
RETURNING CLAUSE IN BULK COLLECT
You can use this to return the processed data to the output
variables or typed variables.
Ex:
DECLARE
type t is table of number(2);
nt t := t(1,2,3,4);
type t1 is table of varchar(2);
nt1 t1;
type t2 is table of student%rowtype;
nt2 t2;
BEGIN
select name bulk collect into nt1 from student;
forall v in nt1.first..nt1.last
E update student set no = nt(v) where
name = nt1(v) returning
no,name,marks
bulk collect into nt2;
for v in nt2.first..nt2.last loop
dbms_output.put_line('Marks = '
|| nt2(v));
end loop;
END;
Output:
Marks
= 100
Marks
= 200
Marks
= 300
Marks
= 400
POINTS TO REMEMBER
1
Cursor
name can be up to 30 characters in length.
2 Cursors
declared in anonymous blocks or subprograms closes automatically when that
block terminates execution.
3
%bulk_rowcount
and %bulk_exceptions can be used only with forall construct.
4
Cursor
declarations may have expressions with column aliases.
5
These
expressions are called virtual columns or calculated columns.
No comments:
Post a Comment