Lab 9.1 Exercises9.1.1 Make Use of Record TypesHere is an example of a record type in an anonymous PL/SQL block. DECLARE vr_zip ZIPCODE%ROWTYPE; BEGIN SELECT * INTO vr_zip FROM zipcode WHERE rownum < 2; DBMS_OUTPUT.PUT_LINE('City: '||vr_zip.city); DBMS_OUTPUT.PUT_LINE('State: '||vr_zip.state); DBMS_OUTPUT.PUT_LINE('Zip: '||vr_zip.zip); END;
CURSOR c_student_name IS SELECT first_name, last_name FROM student; vr_student_name c_student_name%ROWTYPE; In the next Lab you will learn how to process an explicit cursor. Afterward you will address record types within that process. 9.1.2 Process an Explicit Cursor
Opening a CursorThe next step in controlling an explicit cursor is to open it. When the Open cursor statement is processed, the following four actions will take place automatically: The syntax for opening a cursor isOPEN cursor_name;
Fetching Rows in a CursorAfter the cursor has been declared and opened, you can then retrieve data from the cursor. The process of getting the data from the cursor is referred to as fetching the cursor. There are two methods of fetching a cursor, done with the following command:FETCH cursor_name INTO PL/SQL variables; orFETCH cursor_name INTO PL/SQL record; When the cursor is fetched, the following occurs: SET SERVEROUTPUT ON DECLARE CURSOR c_zip IS SELECT * FROM zipcode; vr_zip c_zip%ROWTYPE; BEGIN OPEN c_zip; LOOP FETCH c_zip INTO vr_zip; EXIT WHEN c_zip%NOTFOUND; DBMS_OUTPUT.PUT_LINE(vr_zip.zip|| ' '||vr_zip.city||' '||vr_zip.state); END LOOP; END; The lines in italics have not yet been covered but are essential for the code to run correctly. They will be explained later in this chapter.
Closing a CursorOnce all of the rows in the cursor have been processed (retrieved), the cursor should be closed. This tells the PL/SQL engine that the program is finished with the cursor, and the resources associated with it can be freed. The syntax for closing the cursor isCLOSE cursor_name;
DECLARE CURSOR c_student_name IS SELECT first_name, last_name FROM student WHERE rownum <= 5; vr_student_name c_student_name%ROWTYPE; BEGIN OPEN c_student_name; LOOP FETCH c_student_name INTO vr_student_name; EXIT WHEN c_student_name%NOTFOUND; DBMS_OUTPUT.PUT_LINE('Student name: '|| vr_student_name.first_name||' '||vr_student_name.last_name); END LOOP; CLOSE c_student_name; END;
DECLARE CURSOR c_student_name IS SELECT first_name, last_name FROM student WHERE rownum <= 5; vr_student_name c_student_name%ROWTYPE; BEGIN OPEN c_student_name; LOOP FETCH c_student_name INTO vr_student_name; EXIT WHEN c_student_name%NOTFOUND; END LOOP; CLOSE c_student_name; DBMS_OUTPUT.PUT_LINE('Student name: '|| vr_student_name.first_name||' '||vr_student_name.last_name); END; A programmer-defined record is based on the record type defined by a programmer. First you declare a record type, and next, you declare a record based on the record type defined in the previous step as follows:type type_name IS RECORD (field_name 1 DATATYPE 1, field_name 2 DATATYPE 2, … field_name N DATATYPE N); record_name TYPE_NAME%ROWTYPE; Consider the following code fragment. DECLARE -- declare user-defined type TYPE instructor_info IS RECORD (instructor_id instructor.instructor_id%TYPE, first_name instructor.first_name%TYPE, last_name instructor.last_name%TYPE, sections NUMBER(1)); -- declare a record based on the type defined above rv_instructor instructor_info; In this code fragment, you define your own type, instructor_info. This type contains four attributes: instructor's ID, first and last names, and number of sections taught by this instructor. Next, you declare a record based on the type just described. As a result, this record has structure similar to the type, instructor_ info. Consider the following example. DECLARE TYPE instructor_info IS RECORD (first_name instructor.first_name%TYPE, last_name instructor.last_name%TYPE, sections NUMBER); rv_instructor instructor_info; BEGIN SELECT RTRIM(i.first_name), RTRIM(i.last_name), COUNT(*) INTO rv_instructor FROM instructor i, section s WHERE i.instructor_id = s.instructor_id AND i.instructor_id = 102 GROUP BY i.first_name, i.last_name; DBMS_OUTPUT.PUT_LINE('Instructor, '|| rv_instructor.first_name||' '||rv_instructor.last_name|| ', teaches '||rv_instructor.sections||' section(s)'); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE ('There is no such instructor'); END;
9.1.3 Make Use of Cursor Attributesa) | Now that you know cursor attributes, you can use one of these to exit the loop within the code you developed in the previous example. Are you able to make a fully executable block now? If not, explain why. |
SET SERVEROUTPUT ON
DECLARE
v_city zipcode.city%type;
BEGIN
SELECT city
INTO v_city
FROM zipcode
WHERE zip = 07002;
IF SQL%ROWCOUNT = 1
THEN
DBMS_OUTPUT.PUT_LINE(v_city ||' has a '||
'zipcode of 07002');
ELSIF SQL%ROWCOUNT = 0
THEN
DBMS_OUTPUT.PUT_LINE('The zipcode 07002 is '||
' not in the database');
ELSE
DBMS_OUTPUT.PUT_LINE('Stop harassing me');
END IF;
END;
b)
What will happen if this code is run? Describe what is happening in each phase of the example.
Rerun this block, changing 07002 to 99999. What do you think will happen? Explain.
Now, try running this file. Did it run as you expected? Why or why not? What could be done to improve the way it handles a possible error condition?
9.1.4 Put It All Together
Here is an example of the complete cycle of declaring, opening, fetching, and closing a cursor, including use of cursor attributes.-- ch09_4a.sql
1> DECLARE
2> v_sid student.student_id%TYPE;
3> CURSOR c_student IS
4> SELECT student_id
5> FROM student
6> WHERE student_id < 110;
7> BEGIN
8> OPEN c_student;
9> LOOP
10> FETCH c_student INTO v_sid;
11> EXIT WHEN c_student%NOTFOUND;
12> DBMS_OUTPUT.PUT_LINE('STUDENT ID : '||v_sid);
13> END LOOP;
14> CLOSE c_student;
15> EXCEPTION
16> WHEN OTHERS
17> THEN
18> IF c_student%ISOPEN
19> THEN
20> CLOSE c_student;
21> END IF;
22> END;
| a) | Describe what is happening in each phase of example ch09_4a.sql. Use the line numbers to reference the example. |
| b) | Modify the example to make use of the cursor attributes %FOUND and %ROWCOUNT. |
| c) | Fetch a cursor that has a data from the student table into a %ROWTYPE. Only select students with a student_id under 110. The columns are the STUDENT_ID, LAST_NAME, FIRST_NAME, and a count of the number of classes they are enrolled in (using the enrollment table). Fetch the cursor with a loop and then output all the columns. You will have to use an alias for the enrollment count. |