1.10. Querying DataPL/SQL programs query information from the database with the SQL SELECT statement. Because PL/SQL is tightly integrated with the SQL language, you can execute this SELECT statement natively in your PL/SQL block, as shown below: DECLARE l_employee employee%ROWTYPE; BEGIN SELECT * INTO l_employee FROM employee WHERE employee_id = 7500; END; This SELECT INTO is an example of an implicit cursor, and is just one of several ways you can query data from within a PL/SQL block. You have these choices:
Chapter 2 describes cursors in detail. 1.10.1. Typical Query OperationsRegardless of the type of query cursor, PL/SQL performs the same operations to execute a SQL statement from within your program. In some cases, PL/SQL takes these steps for you. In others, such as with explicit cursors , you will write the code for these steps yourself.
1.10.2. Cursor AttributesPL/SQL offers a set of cursor attributes that can be used to obtain information about the cursor's state. These attributes are shown in Table 1-5. Generally, if you try to reference one of these attributes for a cursor that has not yet been opened, Oracle will raise the INVALID_CURSOR exception.
To reference a cursor attribute, attach it to the name of the cursor or cursor variable about which you want information. Here are some examples:
1.10.3. Implicit CursorsPL/SQL declares and manages an implicit cursor every time you execute a SQL DML statement (INSERT, UPDATE, or DELETE) or a SELECT INTO that returns a single row from the database directly into a PL/SQL data structure. This kind of cursor is called implicit because Oracle implicitly or automatically handles many of the cursor-related operations for you, such as allocating a cursor, opening the cursor, fetching, and so on. An implicit query is a SELECT statement that has the following special characteristics:
The general structure of an implicit query is as follows: SELECT column_list [BULK COLLECT] INTO PL/SQL variable list ...rest of SELECT statement... If you use an implicit cursor, Oracle performs the open, fetches, and close for you automatically; these actions are outside your programmatic control. You can, however, obtain information about the most recently executed SQL statement by examining the values in the implicit SQL cursor attributes, as explained later in "Implicit SQL cursor attributes."
Here is an example of an implicit query that retrieves an entire row of information into a record: DECLARE l_book book%ROWTYPE; BEGIN SELECT * INTO l_book FROM book WHERE isbn = '0-596-00121-5'; 1.10.3.1. Error handling with implicit cursorsThe implicit cursor version of the SELECT statement is a "black box." You pass the SQL statement to the SQL engine in the database, and it returns a single row of information. You can't get inside the separate operations of the cursor, such as the open, fetch, and close stages. You are also stuck with the fact that Oracle will automatically raise exceptions from within the implicit SELECT for two common outcomes:
1.10.3.2. Implicit SQL cursor attributesOracle allows you to access information about the most recently executed implicit cursor by referencing the special implicit cursor attributes shown in Table 1-6. The table describes the significance of the values returned by these attributes for an implicit SQL query (SELECT INTO). Because the cursors are implicit, they have no name and, therefore, the keyword "SQL" is used to denote the implicit cursor. These implicit cursors also apply to implicit DML statements: INSERT, UPDATE, and DELETE.
All the implicit cursor attributes return NULL if no implicit cursors have yet been executed in the session. Otherwise, the values of the attributes always refer to the most recently executed SQL statement, regardless of the block or program from which the SQL statement was executed. 1.10.4. Explicit CursorsAn explicit cursor is a SELECT statement that is explicitly defined in the declaration section of your code and, in the process, assigned a name. There is no such thing as an explicit cursor for UPDATE, DELETE, and INSERT statements. With explicit cursors , you have complete control over the different PL/SQL steps involved in retrieving information from the database. You decide when to OPEN the cursor, when to FETCH records from the cursor (and therefore from the table or tables in the SELECT statement of the cursor), how many records to fetch, and when to CLOSE the cursor. Information about the current state of your cursor is available through examination of cursor attributes. This granularity of control makes the explicit cursor an invaluable tool for your development effort. Let's look at an example. The following function determines (and returns) the level of jealousy I should feel for my friends, based on their location. 1 CREATE OR REPLACE FUNCTION jealousy_level ( 2 NAME_IN IN friends.NAME%TYPE) RETURN NUMBER 3 AS 4 CURSOR jealousy_cur 5 IS 6 SELECT location FROM friends 7 WHERE NAME = UPPER (NAME_IN); 8 9 jealousy_rec jealousy_cur%ROWTYPE; 10 retval NUMBER; 11 BEGIN 12 OPEN jealousy_cur; 13 14 FETCH jealousy_cur INTO jealousy_rec; 15 16 IF jealousy_cur%FOUND 17 THEN 18 IF jealousy_rec.location = 'PUERTO RICO' 19 THEN retval := 10; 20 ELSIF jealousy_rec.location = 'CHICAGO' 21 THEN retval := 1; 22 END IF; 23 END IF; 24 25 CLOSE jealousy_cur; 26 27 RETURN retval; 28 END; This PL/SQL block performs the following cursor actions:
To use an explicit cursor, you must first declare it in the declaration section of your PL/SQL block or in a package, as shown here: CURSOR cursor_name [ ( [ parameter [, parameter ...] ) ] [ RETURN return_specification ] IS SELECT_statement [FOR UPDATE [OF [column list]]; where cursor_name is the name of the cursor, return_specification is an optional RETURN clause for the cursor, and SELECT_statement is any valid SQL SELECT statement. You can also pass arguments into a cursor through the optional parameter list. Once you have declared a cursor, you can OPEN it and FETCH from it. As with implicit cursors, Oracle provides attributes for explicit cursors . Table 1-7 shows you the attribute values you can expect to see both before and after the specified cursor operations.
1.10.5. BULK COLLECTOracle8i Database introduced a very powerful new feature that improves the efficiency of queries in PL/SQL: the BULK COLLECT clause . With BULK COLLECT, you can retrieve multiple rows of data through either an implicit or an explicit query with a single roundtrip to and from the database. BULK COLLECT reduces the number of context switches between the PL/SQL and SQL engines and, thereby, reduces the overhead of retrieving data. The syntax for this clause is: ... BULK COLLECT INTO collection_name[, collection_name] ... where collection_name identifies a collection. Here are some rules and restrictions to keep in mind when using BULK COLLECT:
1.10.5.1. Limiting rows retrieved with BULK COLLECTOracle provides a LIMIT clause for BULK COLLECT that allows you to limit the number of rows fetched from the database. The syntax is: FETCH cursor BULK COLLECT INTO ... [LIMIT rows]; where rows can be any literal, variable, or expression that evaluates to an integer (otherwise, Oracle will raise a VALUE_ERROR exception). LIMIT is very useful with BULK COLLECT, because it helps you manage how much memory your program will use to process data. Suppose, for example, that you need to query and process 10,000 rows of data. You could use BULK COLLECT to retrieve all those rows and populate a rather large collection. However, this approach will consume lots of memory in the Process Global Area for that session. If this code is run by many separate Oracle schemas, your application performance may degrade because of PGA swapping. The following block of code uses the LIMIT clause in a FETCH that is inside a simple loop. Notice that I check the %NOTFOUND attribute after the FETCH to see if any more rows were retrieved. DECLARE CURSOR allrows_cur IS SELECT * FROM EMPLOYEE; TYPE employee_aat IS TABLE OF allrows_cur%ROWTYPE INDEX BY BINARY_INTEGER; l_employees employee_aat; l_row PLS_INTEGER; BEGIN OPEN allrows_cur; LOOP FETCH allrows_cur BULK COLLECT INTO l_employees LIMIT 100; EXIT WHEN allrows_cur%NOTFOUND; -- Process the data by scanning through the collection. l_row := l_employees.FIRST; WHILE (l_row IS NOT NULL) LOOP upgrade_employee_status (l_employees(l_row).employee_id); l_row := l_employees.NEXT (l_row); END LOOP; END LOOP; CLOSE allrows_cur; END; 1.10.6. Cursor Variables and REF CursorsA cursor variable is a variable that points to or references an underlying cursor. Unlike an explicit cursor, which names the PL/SQL work area for the result set, a cursor variable is a reference to that work area. Explicit and implicit cursors are static in that they are tied to specific queries. The cursor variable can be opened for any query, even for different queries within a single program execution. 1.10.6.1. Declaring REF CURSOR typesYou must perform two distinct declaration steps in order to work with a cursor variable:
The syntax for creating a referenced cursor type is as follows: TYPE cursor_type_name IS REF CURSOR [ RETURN return_type ]; where cursor_type_name is the name of the type of cursor, and return_type is the RETURN data specification for the cursor type. The return_type can be any of the data structures valid for a normal cursor RETURN clause and is defined using the %ROWTYPE attribute or by referencing a previously defined record TYPE. Notice that the RETURN clause is optional with the REF CURSOR type statement. Both of the following declarations are valid: TYPE company_curtype IS REF CURSOR RETURN company%ROWTYPE; TYPE generic_curtype IS REF CURSOR; The first form of the REF CURSOR statement is called a strong type because it attaches a record type (or row type) to the cursor variable type at the moment of declaration. Any cursor variable declared using that type can be used only with SQL statement and FETCH INTO data structures that match the specified record type. The advantage of a strong REF TYPE is that the compiler can determine whether or not the developer has properly matched up the cursor variable's FETCH statements with its cursor object's query list. The second form of the REF CURSOR statement, in which the RETURN clause is missing, is called a weak type. This cursor variable type is not associated with any record data structures. Cursor variables declared without the RETURN clause can be used in more flexible ways than the strong type. They can be used with any query, with any rowtype structure, and can vary even within the course of a single program. Starting with Oracle9i Database, Oracle provides a predefined weak REF CURSOR type named SYS_REFCURSOR . You no longer need to define your own weak type; just use Oracle's: DECLARE my_cursor SYS_REFCURSOR; 1.10.6.2. Declaring cursor variablesThe syntax for declaring a cursor variable is: cursor_name cursor_type_name; where cursor_name is the name of the cursor, and cursor_type_name is the name of the type of cursor previously defined with a TYPE statement . Here is an example of the creation of a cursor variable: DECLARE /* Create a cursor type for sports cars. */ TYPE sports_car_cur_type IS REF CURSOR RETURN car%ROWTYPE; /* Create a cursor variable for sports cars. */ sports_car_cur sports_car_cur_type; BEGIN ... END; 1.10.6.3. Opening cursor variablesYou assign a value (the cursor object) to a cursor when you OPEN the cursor. So the syntax for the traditional OPEN statement allows for cursor variables to accept a SELECT statement after the FOR clause, as shown below: OPEN cursor_name FOR SELECT_statement; where cursor_name is the name of a cursor or cursor variable and SELECT_statement is a SQL SELECT statement. For strong REF CURSOR type cursor variables, the structure of the SELECT statement (the number and datatypes of the columns) must match or be compatible with the structure specified in the RETURN clause of the TYPE statement. If cursor_name is a cursor variable defined with a weak REF CURSOR type, you can OPEN it for any query, with any structure. 1.10.6.4. Fetching from cursor variablesAs mentioned earlier, the syntax for a FETCH statement using a cursor variable is the same as that for static cursors: FETCH cursor_variable_name INTO record_name; FETCH cursor_variable_name INTO variable_name, variable_name ...; When the cursor variable is declared with a strong REF CURSOR type, the PL/SQL compiler makes sure the data structures listed after the INTO keyword are compatible with the structure of the query associated with the cursor variable. If the cursor variable is of the weak REF CURSOR type, the PL/SQL compiler cannot perform the same kind of check it performs for a strong REF CURSOR type. Such a cursor variable can FETCH into any data structures because the REF CURSOR type is not identified with a rowtype at the time of declaration. At compile time, there is no way to know which cursor object (and associated SQL statement) will be assigned to that variable. Consequently, the check for compatibility must happen at runtime, when the FETCH is about to be executed. At this point, if the query and the INTO clause do not structurally match, then the PL/SQL runtime engine will raise the predefined ROWTYPE_MISMATCH exception . Note that PL/SQL will use implicit conversions if necessary and possible. |