Chapter 12. Cursor Variables

Beginner

Q:

12-1.

Statement (b) describes a cursor variable. Rather than pointing directly or in a hardcoded way to a result set, a cursor variable is a pointer to a work area in the System Global Area (SGA).

Q:

12-2.

First, you must declare a cursor variable type, also known as a REF CURSOR. Then you declare a variable based on that cursor. The following is an example:

DECLARE
   TYPE emp_t IS REF CURSOR RETURN emp%ROWTYPE;
   emp_cv emp_t;

Q:

12-3.

The declarations are:

  1. Valid. This format declares a type of REF CURSOR that is not tied to any specific format or result set; this is called a weak or nonrestrictive REF CURSOR.

  2. Invalid.

  3. Invalid.

  4. Valid. This format allows you to declare cursor variables that can fetch rows having the same format as the orders table; this is called a strong or restrictive REF CURSOR.

Statements (b) and (c) are invalid. After the REF CURSOR keywords, you either have simply a semicolon or a RETURN rowtype clause, where rowtype is either a %ROWTYPE definition or the name of a previously defined record type.

Q:

12-4.

The declarations are:

  1. Valid. Only (a) is a valid cursor variable declaration based on the disasters_cvt REF CURSOR.

  2. Invalid. This declaration will fail to compile because of the %ROWTYPE attribute. A REF CURSOR type is already a type; you don’t need to %ROWTYPE it.

  3. Invalid. This declaration will compile, but it simply creates a record with the same structure as a row in the environmental_disaster table, not a cursor variable.

Q:

12-5.

Here is a suggested block:

DECLARE
   TYPE emp_cvt IS REF CURSOR RETURN employee%ROWTYPE;
   developer emp_cvt;
   dba emp_cvt;
BEGIN

Q:

12-6.

All cursor operations and cursor attributes work the same for cursor variables as for hardcoded or explicit cursors, except for the OPEN statement (a). When you open a cursor variable, you open it for a specific SELECT statement, as in:

OPEN my_cv FOR SELECT * FROM employee;

Intermediate

Q:

12-7.

Here is one possible implementation:

DECLARE
   TYPE emp_cvt IS REF CURSOR RETURN employee%ROWTYPE;
   employees emp_cvt;
   one_employee employees%ROWTYPE;
BEGIN
   OPEN employees FOR SELECT * FROM employee;
   LOOP
      FETCH employees INTO one_employee;
      EXIT WHEN employees%NOTFOUND;
      double_salary (one_employee.employee_id, one_employee.salary);
   END LOOP;
   CLOSE employees;
END;
/

Q:

12-8.

Here are the rules to keep in mind in solving this problem:

  1. The statement must be valid.

  2. The number and datatypes of the record being declared must be compatible with the record structure in the RETURN clause of the REF CURSOR statement.

The declarations are:

  1. Valid. This statement (as well as (e) and (g)) declares records that can be used in FETCH statements against the_finest_cv cursor variable. This one declares a standard table-based record, exactly the same structure used in the REF CURSOR.

  2. Invalid. Doesn’t compile. You can’t use %ROWTYPE against a REF CURSOR, only against a cursor variable.

  3. Invalid. Doesn’t compile. You can’t declare a record of type REF CURSOR; you can only declare cursor variables based on REF CURSOR types.

  4. Invalid. Doesn’t compile. You need to add %ROWTYPE to this statement.

  5. Valid. This statement relies on a programmer-defined record that has the same structure as the table, so that works just fine.

  6. Invalid. Doesn’t compile. You can’t put a %ROWTYPE at the end of a record TYPE; it already is a type.

  7. Valid. This statement demonstrates that you can declare a record from a cursor variable with %ROWTYPE, just as you can with a table or cursor.

Q:

12-9.

Statements are:

  1. Correct.

  2. Correct.

  3. Incorrect (weakly).

  4. Incorrect (and downright silly).

  5. Correct. When you close a cursor variable, it closes the underlying cursor object.

You should not expect to see any significant difference in runtime performance between cursor variables based on strong and weak REF CURSORs, though the PL/SQL runtime engine will be doing a little more work at runtime with weak REF CURSORs.

Q:

12-10.

Statements (a), (b), and (e) are all “poster children” for the cursor variable feature:

  1. Yes. Cursor variables allow you to “pass” result sets of queries between client and server programs, or between various server programs without actually passing the data. You just pass the pointer to the data. This reduces network traffic.

  2. No. This one requires the use of dynamic SQL (the SQL string is not complete at compile time). You need to use the DBMS_SQL package to implement this scenario, since Method 4 dynamic SQL is requested.

  3. Yes. This is a perfect example of why you might use cursor variables to “hide” minor differences between queries and write a single block of code that works for all of the queries.

  4. Yes.

  5. No. This one also requires dynamic SQL; it can be implemented with DBMS_SQL or native dynamic SQL (if you are running Oracle 8.1).

Q:

12-11.

The problems are:

  1. You cannot declare cursor variables in a package because they do not have persistent state.

  2. The SQL statement cannot be a literal string or a variable. It must be hardcoded or static at the time of program compilation. The OPEN FOR string syntax is used by native dynamic SQL.

  3. You cannot use FOR UPDATE inside a cursor variable’s SELECT statement.

  4. You cannot assign the NULL value to a cursor variable.

Q:

12-12.

The statement:

  1. Raises a ROWTYPE_MISMATCH error when the procedure is executed. You have requested that a record based on the department table receive a row from the employee table. When unhandled, this error displays as:

    ORA-06504: PL/SQL: Return types of Result Set variables or query do not match

    This error always rears its ugly head at runtime (a difficult point at which to debug one’s code) when you rely on weak REF CURSORs.

  2. Fails to compile with this error:

    PLS-00382: expression is of wrong type

    Once again, you have requested that a record based on the department table receive a row from the employee table. This time, however, the compiler can check for compatibility because you have used a strong REF CURSOR.

  3. Doesn’t cause a mismatch error. The airplane_food table has two numeric columns, and your query selects two numeric columns from the employee table.

Q:

12-13.

The function returns a cursor variable, so the RETURN clause of the function must reference a REF CURSOR type. The REF CURSOR type can be defined in the same package as the function (unless the function is to be “standalone”), or it can be declared in a separate package specification. Furthermore, since the REF CURSOR is based on a subset of the columns of the employee table, you also need to define a programmer-defined record type to reflect that two-field structure. Again, this needs to be defined in a package specification, either with the function or independent of it.

Q:

12-14.

Here is one possible implementation of the package specification:


/* Filename on web page: empinfo.pkg */
PACKAGE empinfo
IS
   bysal CONSTANT INTEGER := 1;
   bysaldesc CONSTANT INTEGER := 2;
   bydept CONSTANT INTEGER := 3;
   byname CONSTANT INTEGER := 4;

   TYPE two_pieces_t IS RECORD (str VARCHAR2(100), num NUMBER);
   TYPE emp_cvt IS REF CURSOR RETURN two_pieces_t;
   FUNCTION open (query_number IN INTEGER) RETURN emp_cvt;
END;

Notice that you need to specify only that the record consists of one string and one number. You could have also relied on %TYPE declarations as follows:

TYPE two_pieces_t IS RECORD (
      str employee.last_name%TYPE, num employee.salary%TYPE);

Q:

12-15.

Here is one possible implementation:


/* Filename on web page: empinfo.pkg */
CREATE OR REPLACE PACKAGE BODY empinfo
IS
   FUNCTION open (query_number IN INTEGER) RETURN emp_cvt
   IS
      retval emp_cvt;
   BEGIN
      IF query_number = bysal
      THEN
         OPEN retval FOR
            SELECT last_name, salary FROM employee ORDER BY salary;
      ELSIF query_number = bysaldesc
      THEN
         OPEN retval FOR
            SELECT last_name, salary
              FROM employee ORDER BY salary DESC;
      ELSIF query_number = bydept
      THEN
         OPEN retval FOR
            SELECT last_name, department_id
              FROM employee ORDER BY department_id;
      ELSIF query_number = byname
      THEN
         OPEN retval FOR
            SELECT first_name || ' ' || last_name, salary
              FROM employee ORDER BY last_name;
      END IF;
      RETURN retval;
   END;
END;
/

Q:

12-16.

Here is one possible implementation:

PROCEDURE show (query_number IN INTEGER)
IS
   cv emp_cvt;
   rec cv%ROWTYPE;
BEGIN
   cv := open (query_number);
   LOOP
      FETCH cv INTO rec;
      EXIT WHEN cv%NOTFOUND;
      IF cv%ROWCOUNT = 1
      THEN
         DBMS_OUTPUT.PUT_LINE (RPAD ('-', 60, '-'));
         DBMS_OUTPUT.PUT_LINE ('Contents of Query ' || query_number);
         DBMS_OUTPUT.PUT_LINE (RPAD ('-', 60, '-'));
      END IF;
      DBMS_OUTPUT.PUT_LINE (RPAD (rec.str, 30) || rec.num);
   END LOOP;
   CLOSE cv;
END;

Expert

Q:

12-17.

When you declare a cursor variable as the formal parameter of a subprogram that opens the cursor variable, you must specify the parameter to be of mode OUT or IN OUT. That way, the subprogram can pass an open cursor back to the caller.

Q:

12-18.

Problems are:

  1. You cannot pass a cursor variable through the parameter list of a remote procedure call (RPC). The variable points to a result set in the current database instance.

  2. You cannot use comparison operators to test two or more different cursor variables for equality, inequality, or nullity (IS NULL or IS NOT NULL).

  3. You cannot use a REF CURSOR type to specify the element type of a collection (error PLS-00990). As a consequence, a cursor variable may not be an element in a nested table, index-by table, or VARRAY.

  4. Even through the two different REF CURSOR types have the same RETURN clause, they are considered different types; you cannot, therefore, assign tmp_cv to emp_cv.

Q:

12-19.

The declaration in (a) defines a cursor variable in SQL*Plus of type REF CURSOR (weak). The following is an example of declaring and using such a variable:

SQL> VARIABLE bysal REFCURSOR
SQL> exec :bysal := empinfo.open (empinfo.bysal)
PL/SQL procedure successfully completed.
SQL>

Q:

12-20.

Here is one possible implementation:


/* Filename on web page: cah.pkg */
CREATE OR REPLACE PACKAGE cah
AS
   TYPE tainted_person IS REF CURSOR;

   PROCEDURE identify_person (
      name IN VARCHAR2,
      info_out IN OUT tainted_person
      );
END cah;
/

The most important thing to remember here is that you need a weak REF CURSOR, since each of the table structures is different.

Q:

12-21.

The following code is one possible implementation. Notice that a standard explicit cursor is used to first determine the type of involvement of the person in a crime against humanity. Once you have the type, you can use an IF statement to perform a SELECT from the appropriate table. If you don’t find a match, make sure that the cursor variable you return to the calling program is closed.


/* Filename on web page: cah.pkg */
PROCEDURE identify_person (
   name IN VARCHAR2,
   info_out IN OUT tainted_person
   )
IS
   CURSOR cah_type_cur IS
      SELECT cah_type
        FROM person
       WHERE name = identify_person.name;

   v_type person.cah_type%TYPE;
BEGIN
   OPEN cah_type_cur;
   FETCH cah_type_cur INTO v_type;

   IF cah_type_cur%NOTFOUND
   THEN
      /* Close the CV if it is open to indicate that the
         name did not provide valid results. */
      IF info_out%ISOPEN THEN CLOSE info_out; END IF;
   ELSIF v_type = 1
   THEN
      OPEN info_out FOR SELECT * FROM direct_combatant
         WHERE name = identify_person.name;
   ELSIF v_type = 1
   THEN
      OPEN info_out FOR SELECT * FROM indirect_enabler
         WHERE name = identify_person.name;
   ELSIF v_type = 1
   THEN
      OPEN info_out FOR SELECT * FROM noninnocent_bystander
         WHERE name = identify_person.name;
   END IF;
   CLOSE cah_type_cur;
END identify_person;
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.26.90