What are Cursors?
A cursor is a pointer to a private SQL area that stores information about the processing of a SELECT statement or a DML statement (Insert, Update or Delete). In layman terms, a Cursor is used to fetch (SELECT) a particular dataset from a much larger dataset which can then be used for data manipulation using other DML Operations like Insert, Update or Delete.
There are two types of Cursors: Implicit Cursors and Explicit Cursors.
In this post, we will discuss mainly about Explicit Cursors.
Explicit Cursors: Unlike Implicit cursors, application developers have control over the life cycle of the explicit cursors by manually performing the OPEN and FETCH operations and then CLOSE the Cursor.
A Cursor is defined in the declarative part of the PL/SQL Block and the following are three stages are part of the Cursor life cycle.
- OPEN
- Open: Database allocates a private work area in the user’s session memory for cursor processing.
- Parse SQL: Validates the SQL query for syntax and privileges.
- Bind SQL: Provides an input value to the bind variables that are part of the query.
- Executes the SQL Statement (that has the required conditions in the WHERE condition).
- FETCH
- Fetches the data from the parsed SQL statement. Fetches the data into a block variable and increments the record pointer.
- CLOSE
- Closes the cursor and releases the memory back to System Global Area (SGA).
Consider the following dataset to demonstrate an example of an Explicit cursor. Create a sample table and insert data into the table.
CREATE TABLE xx_test_cursor ( emp_name VARCHAR2 (200), emp_id NUMBER, emp_join_date DATE, manager_name VARCHAR2 (200) ); INSERT INTO xx_test_cursor VALUES ('John',1001,SYSDATE - 30, 'Frank'); INSERT INTO xx_test_cursor VALUES ('Graeme', 1002, SYSDATE - 40, 'Frank'); INSERT INTO xx_test_cursor VALUES ('Shawn', 1003, SYSDATE - 50, 'Frank'); INSERT INTO xx_test_cursor VALUES ('Jonty', 1004, SYSDATE - 60, 'Frank'); INSERT INTO xx_test_cursor VALUES ('Mike', 1007, SYSDATE - 60, 'Frank'); INSERT INTO xx_test_cursor VALUES ('Peter', 1008, SYSDATE - 60, 'Justin'); INSERT INTO xx_test_cursor VALUES ('Jason', 1009, SYSDATE - 60, 'Justin'); INSERT INTO xx_test_cursor VALUES ('Glenn', 1010, SYSDATE - 60, 'Justin'); INSERT INTO xx_test_cursor VALUES ('Adam', 1011, SYSDATE - 60, 'Justin'); INSERT INTO xx_test_cursor VALUES ('Chris', 1012, SYSDATE - 60, 'Justin'); COMMIT;
SELECT * FROM xx_test_cursor order by emp_id; EMP_NAME EMP_ID EMP_JOIN_DATE MANAGER_NAME John 1,001 08/27/2018 19:01:10 Frank Graeme 1,002 08/17/2018 19:01:11 Frank Shawn 1,003 08/07/2018 19:01:12 Frank Jonty 1,004 07/28/2018 19:01:13 Frank DARELL 1,005 07/28/2018 19:23:58 Frank Mike 1,007 07/31/2018 01:50:23 Justin Peter 1,008 07/31/2018 01:50:24 Justin Jason 1,009 07/31/2018 01:50:26 Justin Glenn 1,010 07/31/2018 01:50:27 Justin Adam 1,011 07/31/2018 01:50:28 Justin
The following code will display the data from the table in the required format.
Cursor – FETCH
- Code from Line Number 2 to 5 is where the code is being defined in the declare section.
- Code in line number 13 opens the cursor.
- Code in line number 14 fetches the data from the SQL cursor into a record variable that has a cursor type datatype.
- Once all the data is fetched, code in line number 15 closes the Cursor.
- Code from Line 17 to 29, checks if there is any data in the record variable, then loops through the record using an index, and displays the data in the required format, and then closes the loop.
DECLARE CURSOR c_data IS SELECT emp_name, emp_join_date, manager_name FROM xx_test_cursor; TYPE rec_data_type IS TABLE OF c_data%ROWTYPE INDEX BY BINARY_INTEGER; l_data rec_data_type; BEGIN OPEN c_data; FETCH c_data BULK COLLECT INTO l_data; CLOSE c_data; IF l_data.COUNT > 0 THEN FOR i IN l_data.FIRST .. l_data.LAST LOOP DBMS_OUTPUT.put_line ( 'Employee: ' || l_data (i).emp_name || ' Join date is: ' || l_data (i).emp_join_date || ' and Manager is: ' || l_data (i).manager_name); END LOOP; END IF; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ('Exception when processing data: ' || SQLERRM); END;
Corresponding Dbms_output is: Employee: Mike Join date is: 31-JUL-18 and Manager is: Justin Employee: Peter Join date is: 31-JUL-18 and Manager is: Justin Employee: Jason Join date is: 31-JUL-18 and Manager is: Justin Employee: Glenn Join date is: 31-JUL-18 and Manager is: Justin Employee: Adam Join date is: 31-JUL-18 and Manager is: Justin Employee: John Join date is: 27-AUG-18 and Manager is: Frank Employee: Graeme Join date is: 17-AUG-18 and Manager is: Frank Employee: Shawn Join date is: 07-AUG-18 and Manager is: Frank Employee: Jonty Join date is: 28-JUL-18 and Manager is: Frank Employee: DARELL Join date is: 28-JUL-18 and Manager is: Frank
FOR LOOP:
DECLARE CURSOR c_data IS SELECT emp_name, emp_join_date, manager_name FROM xx_test_cursor; BEGIN FOR rec_data IN c_data LOOP DBMS_OUTPUT.put_line ( 'Employee: ' || rec_data.emp_name || ' Join date is: ' || rec_data.emp_join_date || ' and Manager is: ' || rec_data.manager_name); END LOOP; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.put_line ('Exception when processing data: ' || SQLERRM); END;
Corresponding dbms_output is: Employee: Mike Join date is: 31-JUL-18 and Manager is: Justin Employee: Peter Join date is: 31-JUL-18 and Manager is: Justin Employee: Jason Join date is: 31-JUL-18 and Manager is: Justin Employee: Glenn Join date is: 31-JUL-18 and Manager is: Justin Employee: Adam Join date is: 31-JUL-18 and Manager is: Justin Employee: John Join date is: 27-AUG-18 and Manager is: Frank Employee: Graeme Join date is: 17-AUG-18 and Manager is: Frank Employee: Shawn Join date is: 07-AUG-18 and Manager is: Frank Employee: Jonty Join date is: 28-JUL-18 and Manager is: Frank Employee: DARELL Join date is: 28-JUL-18 and Manager is: Frank