Q: | |
10-6. | Choices (b), (e), (f), and (i) are all valid cursor attributes. They return the following information: % ROWCOUNTReturns the number of rows processed by the cursor at that point (this number might reflect the number of rows fetched or the number of rows processed—that is, deleted, updated, or inserted). - %ISOPEN
Is a Boolean attribute that returns TRUE when a cursor is open. - %FOUND
Is a Boolean attribute that returns TRUE if the last fetch returned a row and FALSE if not. - %NOTFOUND
Is a Boolean attribute that returns TRUE if the last fetch did not return a row and FALSE if it did.
For explicit cursors, they are specified as cursor name%attribute ; for example, CURS_GET_EMPS%FOUND. For implicit cursors, they are specified as SQL%attribute ; for example, SQL%NOTFOUND. |
Q: | |
10-7. | The usages are: Invalid. It raises an INVALID_CURSOR exception, since the cursor has not been opened, and you are checking the FOUND attribute. Valid. Invalid. You’re referencing the %ROWCOUNT attribute as though it were a Boolean expression. It is, in fact, a numeric attribute. Invalid. You’re applying the attribute to a record instead of the cursor. Invalid. You’re applying the attribute to the table name rather the “SQL” keyword, as is necessary with implicit attributes.
|
Q: | |
10-8. | You are executing the CLOSE after the RETURN statement, so in fact that CLOSE statement is never run. It would therefore seem that your cursor would remain open and you would run out of open cursors in your session. That is not, however, the case. Since your cursor is declared locally, it is automatically closed when the function returns its value. You can leave the CLOSE statement out entirely, and the function will work. |
Q: | |
10-9. | The loyal employee cannot be identified by this code alone. It might not even be the same one each time unless the same index is always used. The point is that the first employee returned by the query always receives the 10% raise regardless of which employee_id is passed to the function. The cursor parameter is ignored within the query that simply equates the employee_id column to the employee_id column that is true for all rows. In an effort to appease the other angry employees (and disappoint a few ecstatic ones) the cursor should be changed to avoid the naming conflict between the parameter and the column. Here is one possibility: CURSOR curs_get_curr_sal (cp_employee_id employee.employee_id%TYPE )
IS
SELECT salary
FROM employee
WHERE employee_id = cp_employee_id
FOR UPDATE OF salary; This clearly differentiates the cursor parameter from the table column. |
Q: | |
10-10. | The output is as follows: %FOUND-TRUE
%NOTFOUND-FALSE
%ISOPEN-FALSE
%ROWCOUNT-1 These are the four attributes of implicit cursors. |
Q: | |
10-11. | The cursor attributes %FOUND and %NOTFOUND come in handy here. If a row is returned, the %FOUND is TRUE, and %NOTFOUND is FALSE. Conversely, %FOUND is FALSE and %NOTFOUND is TRUE when no rows are returned. Either one can be used in this case. DECLARE
CURSOR curs_failure IS
SELECT null
FROM employee
WHERE 1 = 2;
v_dummy VARCHAR2(1);
BEGIN
OPEN curs_failure;
FETCH curs_failure INTO v_dummy;
IF curs_failure%NOTFOUND
THEN
DBMS_OUTPUT.put_line (
'It is official. 1 ' ||
'does not equal 2!' );
ELSE
DBMS_OUTPUT.put_line (
'It is still official. ' ||
'1 does not equal 2!' );
END IF;
CLOSE curs_failure;
END; |
Q: | |
10-12. | The %ROWCOUNT cursor attribute is incremented every time a row is returned by the cursor. It can be used to process a select number of rows from the query: CREATE OR REPLACE FUNCTION three_highest RETURN NUMBER
IS
-- Cursor to get the salaries in descending order
CURSOR curs_get_salary IS
SELECT NVL (salary, 0) salary
FROM employee
ORDER BY salary desc;
v_salary_rec curs_get_salary%ROWTYPE;
v_ret_val NUMBER := 0;
BEGIN
/*
|| Use the cursor to fetch rows and increment the
|| salary total. Stop after processing the third row.
*/
OPEN curs_get_salary;
LOOP
FETCH curs_get_salary INTO v_salary_rec;
EXIT WHEN curs_get_salary%NOTFOUND; -- Fewer than 3 rows?
v_ret_val := v_ret_val + v_salary_rec.salary;
EXIT WHEN curs_get_salary%ROWCOUNT = 3;
END LOOP;
CLOSE curs_get_salary;
RETURN(v_ret_val);
END three_highest; |
Q: | |
10-13. | You should always take the approach shown in (b): do not declare and fetch into individual variables. Instead, declare a record based on the cursor and fetch into the record. You can then reference whichever fields you need. |
Q: | |
10-14. | You see the following output: 32
MURRAY This program does not raise a NO_DATA_FOUND error, even though it is fetching well beyond the end of the result set of this cursor. An explicit FETCH past the last row simply has no effect; the contents of the record (rec in this case) remains unchanged. |
Q: | |
10-15. | You can remove the CLOSE statement, and the code executes in exactly the same way—and the cursor closes. If you declare a cursor in a local block, its scope and visibility are restricted to this block (or procedure or function). When the block finishes executing, the memory associated with the cursor and any other local data structures is released. |
Q: | |
10-16. | Since you are fetching and processing each and every row identified by the cursor, you can use a cursor FOR loop and delete much of the code you previously wrote: DECLARE
CURSOR brought_to_you_by_unions
IS
SELECT weekends, forty_hour_weeks, overtime_pay
FROM business_practices;
BEGIN
FOR social_benefits IN brought_to_you_by_unions
LOOP
calculate_impact (social_benefits);
END LOOP;
END; |
Q: | |
10-17. | The example shown in the problem most definitely does not do the trick. It, instead, raises the following error: ORA-01001: invalid cursor When you reference the %ROWCOUNT attribute, the cursor has already been opened, fetched from, and closed. That is the trade-off with a cursor FOR loop: it makes it easy to write the body of your loop, but outside of your loop, you are in the dark about what just happened. To obtain the count, you have to create a local variable and keep count yourself. Here is one implementation: DECLARE
CURSOR brought_to_you_by_unions
IS
SELECT weekends, forty_hour_weeks, overtime_pay
FROM business_practices;
v_count PLS_INTEGER;
BEGIN
FOR social_benefits IN brought_to_you_by_unions
LOOP
calculate_impact (social_benefits);
v_count := brought_to_you_by_unions%ROWCOUNT;
END LOOP;
DBMS_OUTPUT.PUT_LINE (
'Calculated impact of ' || NVL (v_count, 0) -- in case of NOT FOUND
|| ' benefits brought about by unions.'),
END; |