I. Concepts
A cursor is a memory workspace of SQL, defined by the system or user as a variable. The function of cursors is to temporarily store blocks of data extracted from databases. In some cases, data needs to be transferred from tables stored on disk to computer memory for processing, and finally the processing results are displayed or written back to the database. In this way, the speed of data processing will be improved, otherwise frequent disk data exchange will reduce efficiency.
Types
Cursor types include three types: implicit Cursor, explicit Cursor and Ref Cursor (dynamic Cursor).
1. Implicit Cursor:
For Select... INTO... Statement, only one data can be retrieved from the database at a time. For this type of DML Sql statement, it is implicit Cursor. For example: Select/Update/Insert/Delete operation.
2) Function: Implicit Cusor attributes can be used to understand the operation status and results, so as to achieve process control. Cursor's attributes include:
The SQL%ROWCOUNT integer represents the number of rows of data successfully executed by the DML statement
The SQL%FOUND Boolean value TRUE represents the success of insertion, deletion, update or single-line query operations.
The Boolean type of SQL%NOTFOUND is contrary to the return value of the attribute of SQL%FOUND.
SQL%ISOPEN Boolean DML is true during execution and false after execution.
3) Implicit Cursor is a system that automatically opens and closes Cursor.
Here is a Sample:
-
Set Serveroutput on;
-
-
begin
-
update t_contract_master set liability_state = 1 where policy_code = '123456789';
-
-
if SQL%Found then
-
dbms_output.put_line('the Policy is updated successfully.');
-
commit;
-
else
-
dbms_output.put_line('the policy is updated failed.');
-
end if;
-
-
end;
-
-
/
2. Explicit Cursor:
(1) For extracting multi-row data from the database, explicit Cursor is needed. The properties of explicit cursor include:
Property of cursor, return value type, meaning
% ROWCOUNT Integer Gets the Number of Data Rows Returned by FETCH Statement
% The recent FOUND Boolean FETCH statement that returns a row of data is true or false.
% NOTFOUND Boolean and% FOUND attribute return values are opposite.
% ISOPEN Boolean cursor is true when it has been opened, otherwise it is false.
(2) The application of explicit cursors is divided into four steps:
Define cursor - Cursor [Cursor Name] IS;
Open the cursor - Open [Cursor Name];
Operational data - Fetch [Cursor name]
Close the cursor --- Close [Cursor Name]. This Step must not be omitted.
(3) The following are three common explicit Cursor uses.
-
Set serveroutput on;
-
-
declare
-
---define Cursor
-
Cursor cur_policy is
-
select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account
-
from t_contract_master cm
-
where cm.liability_state = 2
-
and cm.policy_type = 1
-
and cm.policy_cate in ('2','3','4')
-
and rownum < 5
-
order by cm.policy_code desc;
-
curPolicyInfo cur_policy%rowtype;---Define cursor variables
-
Begin
-
open cur_policy; ---open cursor
-
Loop
-
--deal with extraction data from DB
-
Fetch cur_policy into curPolicyInfo;
-
Exit when cur_policy%notfound;
-
-
Dbms_Output.put_line(curPolicyInfo.policy_code);
-
end loop;
-
Exception
-
when others then
-
close cur_policy;
-
Dbms_Output.put_line(Sqlerrm);
-
-
if cur_policy%isopen then
-
--close cursor
-
close cur_policy;
-
end if;
-
end;
-
-
/
-
------------------------
-
-
Set serveroutput on;
-
-
declare
-
Cursor cur_policy is
-
select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account
-
from t_contract_master cm
-
where cm.liability_state = 2
-
and cm.policy_type = 1
-
and cm.policy_cate in ('2','3','4')
-
and rownum < 5
-
order by cm.policy_code desc;
-
v_policyCode t_contract_master.policy_code%type;
-
v_applicantId t_contract_master.applicant_id%type;
-
v_periodPrem t_contract_master.period_prem%type;
-
v_bankCode t_contract_master.bank_code%type;
-
v_bankAccount t_contract_master.bank_account%type;
-
Begin
-
open cur_policy;
-
Loop
-
Fetch cur_policy into v_policyCode,
-
v_applicantId,
-
v_periodPrem,
-
v_bankCode,
-
v_bankAccount;
-
Exit when cur_policy%notfound;
-
-
Dbms_Output.put_line(v_policyCode);
-
end loop;
-
Exception
-
when others then
-
close cur_policy;
-
Dbms_Output.put_line(Sqlerrm);
-
-
if cur_policy%isopen then
-
close cur_policy;
-
end if;
-
end;
-
/
-
--------------------------------
-
Set serveroutput on;
-
-
declare
-
Cursor cur_policy is
-
select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account
-
from t_contract_master cm
-
where cm.liability_state = 2
-
and cm.policy_type = 1
-
and cm.policy_cate in ('2','3','4')
-
and rownum < 5
-
order by cm.policy_code desc;
-
Begin
-
For rec_Policy in cur_policy loop
-
Dbms_Output.put_line(rec_policy.policy_code);
-
end loop;
-
Exception
-
when others then
-
Dbms_Output.put_line(Sqlerrm);
-
-
end;
-
-
/
3. Ref Cursor (Dynamic Cursor):
1) The difference between implicit Cursor and explicit Cursor is that Ref Cursor can obtain data result sets by passing parameters during operation. The other two Cursors, which are static, determine the data result set during compilation.
2) Ref cursor use:
Type [Cursor type name] is ref cursor
Define dynamic Sql statement
Open cursor
Operational data - Fetch [Cursor name]
Close Cursor
Here is a Sample:
-
Set serveroutput on;
-
-
Declare
-
---define cursor type name
-
type cur_type is ref cursor;
-
cur_policy cur_type;
-
sqlStr varchar2(500);
-
rec_policy t_contract_master%rowtype;
-
begin
-
---define dynamic Sql
-
sqlStr := 'select cm.policy_code, cm.applicant_id, cm.period_prem,cm.bank_code,cm.bank_account from t_contract_master cm
-
where cm.liability_state = 2
-
and cm.policy_type = 1
-
and cm.policy_cate in (2,3,4)
-
and rownum < 5
-
order by cm.policy_code desc ';
-
---Open Cursor
-
open cur_policy for sqlStr;
-
loop
-
fetch cur_policy into rec_policy.policy_code, rec_policy.applicant_id, rec_policy.period_prem,rec_policy.bank_code,rec_policy.bank_account;
-
exit when cur_policy%notfound;
-
-
Dbms_Output.put_line('Policy_code:'||rec_policy.policy_code);
-
-
end loop;
-
close cur_policy;
-
-
end;
-
/
4. Common Exception s
-
1. Error, name, code, meaning
-
2. CURSOR_ALREADY_OPEN ORA_06511 Attempts to Open the Opened Cursor
-
3. INVALID_CURSOR ORA_01001 Attempt to use an open cursor
-
4. DUP_VAL_ON_INDEX ORA_00001 Store duplicate values in columns with unique index constraints
-
5. ZERO_DIVIDE ORA_01476. A division error with a divisor of zero occurs.
-
6. INVALID_NUMBER ORA_01722 Attempts to Numerically Convert Invalid Characters
-
7. ROWTYPE_MISMATCH ORA_06504. Types of main variables and cursors are incompatible.
-
8. VALUE_ERROR ORA_06502 Conversion, Truncation or Arithmetic Error
-
9. TOO_MANY_ROWS ORA_01422 SELECT... INTO... Statement returns more than one row of data.
-
10. NO_DATA_FOUND ORA_01403 SELECT... INTO... Statement with no data returned
-
11. TIMEOUT_ON_RESOURCE ORA_00051. A timeout error occurred while waiting for resources.
-
12. TRANSACTION_BACKED_OUT ORA_00060 Delivery failed due to deadlock
-
13. STORAGE_ERROR ORA_06500 Memory error occurred
-
14. PL/SQL internal error occurred in PROGRAM_ERROR ORA_06501
-
15. NOT_LOGGED_ON ORA_01012 Attempt to operate an unconnected database
-
16. LOGIN_DENIED ORA_01017 provides invalid username or password at connection time
Attachment: Using cursors
-
15-1:Receiving cursor data using scalar variables
-
DECLARE
-
CURSOR emp_cursor IS
-
SELECT ename,job,sal FROM emp WHERE deptno=&dno;
-
v_ename emp.ename%TYPE;
-
v_sal emp.sal%TYPE;
-
v_job emp.job%TYPE;
-
BEGIN
-
OPEN emp_cursor;
-
LOOP
-
FETCH emp_cursor INTO v_ename,v_job,v_sal;
-
EXIT WHEN emp_cursor%NOTFOUND;
-
dbms_output.put_line('Full name:'||v_ename||',Post:'||v_job||',Wages:'||v_sal);
-
END LOOP;
-
CLOSE emp_cursor;
-
END;
-
/
-
15-2:Use pl/sql Recording variable receives cursor data
-
DECLARE
-
CURSOR emp_cursor IS
-
SELECT ename,sal FROM emp ORDER BY sal DESC;
-
emp_record emp_cursor%ROWTYPE;
-
BEGIN
-
OPEN emp_cursor;
-
LOOP
-
FETCH emp_cursor INTO emp_record;
-
EXIT WHEN emp_cursor%NOTFOUND OR emp_cursor%ROWCOUNT>&n;
-
dbms_output.put_line('Full name:'||emp_record.ename||
-
',Wages:'||emp_record.sal);
-
END LOOP;
-
CLOSE emp_cursor;
-
END;
-
/
-
15-3:Use pl/sql Collection variable receives cursor data
-
DECLARE
-
CURSOR emp_cursor IS SELECT ename,sal FROM emp
-
WHERE lower(job)=lower('&job');
-
TYPE emp_table_type IS TABLE OF emp_cursor%ROWTYPE
-
INDEX BY BINARY_INTEGER;
-
emp_table emp_table_type;
-
i INT;
-
BEGIN
-
OPEN emp_cursor;
-
LOOP
-
i:=emp_cursor%ROWCOUNT+1;
-
FETCH emp_cursor INTO emp_table(i);
-
EXIT WHEN emp_cursor%NOTFOUND;
-
dbms_output.put_line('Full name:'||emp_table(i).ename||
-
',Wages:'||emp_table(i).sal);
-
END LOOP;
-
CLOSE emp_cursor;
-
END;
-
/
-
15-4:CursorforReference to defined cursors in loops
-
DECLARE
-
CURSOR emp_cursor IS SELECT ename,hiredate FROM emp
-
ORDER BY hiredate DESC;
-
BEGIN
-
FOR emp_record IN emp_cursor LOOP
-
dbms_output.put_line('Full name:'||emp_record.ename
-
||',Date of work:'||emp_record.hiredate);
-
EXIT WHEN emp_cursor%ROWCOUNT=&n;
-
END LOOP;
-
END;
-
/
-
15-5:CursorforDirect reference subqueries in loops
-
BEGIN
-
FOR emp_record IN (SELECT ename,hiredate,rownum FROM emp
-
ORDER BY hiredate) LOOP
-
dbms_output.put_line('Full name:'||emp_record.ename
-
||',Date of work:'||emp_record.hiredate);
-
EXIT WHEN emp_record.rownum=&n;
-
END LOOP;
-
END;
-
/
-
15-6:Parameter cursor
-
DECLARE
-
CURSOR emp_cursor(dno NUMBER) IS
-
SELECT ename,job FROM emp WHERE deptno=dno;
-
BEGIN
-
FOR emp_record IN emp_cursor(&dno) LOOP
-
dbms_output.put_line('Full name:'||emp_record.ename
-
||',Post:'||emp_record.job);
-
END LOOP;
-
END;
-
/
-
15-7:Update cursor rows
-
DECLARE
-
CURSOR emp_cursor IS
-
SELECT ename,sal,deptno FROM emp FOR UPDATE;
-
dno INT:=&no;
-
BEGIN
-
FOR emp_record IN emp_cursor LOOP
-
IF emp_record.deptno=dno THEN
-
dbms_output.put_line('Full name:'||emp_record.ename
-
||',Original wages:'||emp_record.sal);
-
UPDATE emp SET sal=sal*1.1 WHERE CURRENT OF emp_cursor;
-
END IF;
-
END LOOP;
-
END;
-
/
-
15-8:Delete cursor rows
-
DECLARE
-
CURSOR emp_cursor IS
-
SELECT ename FROM emp FOR UPDATE;
-
name VARCHAR2(10):=lower('&name');
-
BEGIN
-
FOR emp_record IN emp_cursor LOOP
-
IF lower(emp_record.ename)=name THEN
-
DELETE FROM emp WHERE CURRENT OF emp_cursor;
-
ELSE
-
dbms_output.put_line('Full name:'||emp_record.ename);
-
END IF;
-
END LOOP;
-
END;
-
/
-
15-9:Use of Clauses add row-sharing locks on a particular table.
-
DECLARE
-
CURSOR emp_cursor IS
-
SELECT a.dname,b.ename FROM dept a JOIN emp b
-
ON a.deptno=b.deptno
-
FOR UPDATE OF b.deptno;
-
name VARCHAR2(10):=LOWER('&name');
-
BEGIN
-
FOR emp_record IN emp_cursor LOOP
-
IF LOWER(emp_record.dname)=name THEN
-
dbms_output.put_line('Full name:'||emp_record.ename);
-
DELETE FROM emp WHERE CURRENT OF emp_cursor;
-
END IF;
-
END LOOP;
-
END;
-
/
-
15-10:Use cursor variables with no return type
-
DECLARE
-
TYPE ref_cursor_type IS REF CURSOR;
-
ref_cursor ref_cursor_type;
-
v1 NUMBER(6);
-
v2 VARCHAR2(10);
-
BEGIN
-
OPEN ref_cursor FOR
-
SELECT &col1 col1,&col2 col2 FROM &table WHERE &cond;
-
LOOP
-
FETCH ref_cursor INTO v1,v2;
-
EXIT WHEN ref_cursor%NOTFOUND;
-
dbms_output.put_line('col1='||v1||',col2='||v2);
-
END LOOP;
-
CLOSE ref_cursor;
-
END;
-
/
-
15-11:Use cursor variables with return types
-
DECLARE
-
TYPE emp_cursor_type IS REF CURSOR RETURN emp%ROWTYPE;
-
emp_cursor emp_cursor_type;
-
emp_record emp%ROWTYPE;
-
BEGIN
-
OPEN emp_cursor FOR SELECT * FROM emp
-
WHERE deptno=&dno;
-
LOOP
-
FETCH emp_cursor INTO emp_record;
-
EXIT WHEN emp_cursor%NOTFOUND;
-
dbms_output.put_line('Full name:'||emp_record.ename||
-
',wages:'||emp_record.sal);
-
END LOOP;
-
CLOSE emp_cursor;
-
END;
-
/
-
15-12:Use fetch...bulk collect Extract all data
-
DECLARE
-
CURSOR emp_cursor IS
-
SELECT * FROM emp WHERE LOWER(job)=LOWER('&job');
-
TYPE emp_table_type IS TABLE OF emp%ROWTYPE;
-
emp_table emp_table_type;
-
BEGIN
-
OPEN emp_cursor;
-
FETCH emp_cursor BULK COLLECT INTO emp_table;
-
CLOSE emp_cursor;
-
FOR i IN 1.. emp_table.COUNT LOOP
-
dbms_output.put_line('Full name:'||emp_table(i).ename
-
||',wages:'||emp_table(i).sal);
-
END LOOP;
-
END;
-
/
-
15-13:Use limit Clause restriction on the number of rows extracted
-
DECLARE
-
CURSOR emp_cursor IS SELECT * FROM emp;
-
TYPE emp_array_type IS VARRAY(5) OF emp%ROWTYPE;
-
emp_array emp_array_type;
-
BEGIN
-
OPEN emp_cursor;
-
LOOP
-
FETCH emp_cursor BULK COLLECT INTO emp_array LIMIT &rows;
-
FOR i IN 1..emp_array.COUNT LOOP
-
dbms_output.put_line('Full name:'||emp_array(i).ename
-
||',wages:'||emp_array(i).sal);
-
END LOOP;
-
EXIT WHEN emp_cursor%NOTFOUND;
-
END LOOP;
-
CLOSE emp_cursor;
-
END;
-
/
-
15-14:Use cursor Expression
-
DECLARE
-
CURSOR dept_cursor(no NUMBER) IS
-
SELECT a.dname,CURSOR(SELECT * FROM emp
-
WHERE deptno=a.deptno)
-
FROM dept a WHERE a.deptno=no;
-
TYPE ref_cursor_type IS REF CURSOR;
-
emp_cursor ref_cursor_type;
-
emp_record emp%ROWTYPE;
-
v_dname dept.dname%TYPE;
-
BEGIN
-
OPEN dept_cursor(&dno);
-
LOOP
-
FETCH dept_cursor INTO v_dname,emp_cursor;
-
EXIT WHEN dept_cursor%NOTFOUND;
-
dbms_output.put_line('Department name:'||v_dname);
-
LOOP
-
FETCH emp_cursor INTO emp_record;
-
EXIT WHEN emp_cursor%NOTFOUND;
-
dbms_output.put_line('----Employee name:'||emp_record.ename
-
||',post:'||emp_record.job);
-
END LOOP;
-
END LOOP;
-
CLOSE dept_cursor;
-
END;
-
/