15.2. Working with 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 cursor is a SELECT statement that has these special characteristics:
The general structure of an implicit query is as follows:
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 this chapter.
15.2.1. Implicit Cursor ExamplesA common use of implicit cursors
Once I have fetched the title into my local variable, l_title, I can manipulate that informationfor example, by changing the variable's value, displaying the title, or passing the title on to another PL/SQL program for processing. Here is an example of an implicit query that retrieves an entire row of information into a record:
You can also retrieve group-level information from a query. The following single-row query calculates and returns the total salary for a department. Once again, PL/SQL creates an implicit cursor for this statement:
Because PL/SQL is so tightly integrated with the Oracle database, you can also easily retrieve complex datatypes, such as objects and collections, within your implicit cursor. All of these illustrate the use of implicit queries to retrieve a single row's worth of information. If you want to retrieve more than one row, you must use either an explicit cursor for that query or the BULK COLLECT INTO clause
15.2.2. Error Handling with Implicit CursorsThe implicit cursor version of the SELECT statement is kind of 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 automatically raises exceptions from within the implicit SELECT for two common outcomes:
When either of these scenarios occurs (as well as any other exceptions raised when executing a SQL statement), execution of the current block terminates and control is passed to the exception section. You have no control over this process flow; you cannot tell Oracle that with this implicit cursor you actually expect to not find any rows, and it is not an error. Instead, whenever you code an implicit cursor, you should include an exception section that traps and handles these two exceptions (and perhaps others, depending on your application logic). In the following block of code, I query the title of a book based on its ISBN number, but I also anticipate the possible problems that arise:
One of the problems with using implicit queries is that there is an awesome temptation to make assumptions about the data being retrieved, such as:
The consequence of such assumptions is often that we neglect to include exception handlers for our implicit queries. Now, it may well be true that today, with the current set of data, a query will return only a single row. If the nature of the data ever changes, however, you may find that the SELECT statement that formerly identified a single row now returns several. Your program will raise an exception, the exception will not be properly handled, and this could cause problems in your code. You should, as a rule, always include handlers for NO_DATA_FOUND and TOO_MANY_ROWS whenever you write an implicit query. More generally, you should include error handlers for any errors that you can reasonably anticipate will occur in your program. The action you take when an error does arise will vary. Consider the code that retrieves a book title for an ISBN. In the function below, notice that my two error handlers act very differently: NO_DATA_FOUND returns a value, while TOO_MANY_ROWS logs the error and reraises the exception, causing the function to actually fail. (See Chapter 6 for more information about the errpkg.pkg package.)
Here is the reasoning behind these varied treatments: the point of my function is to return the name of a book, which can never be NULL. The function can also be used to validate an ISBN (e.g., "does a book exist for this ISBN?"). For this reason, I really don't want my function to raise an exception when no book is found for an ISBN; that may actually constitute a successful condition, depending on how the function is being used. The logic may be, "If a book does not exist with this ISBN, then it can be used for a new book," which might be coded as:
In other words, the fact that no book exists for that ISBN is not an error and should not be treated as one within my general lookup function. On the other hand, if the query raises the TOO_MANY_ROWS exception, I have a real problem: there should never be two different books with the same ISBN number. So in this case, I need to log the error and then stop the application. 15.2.3. Implicit SQL Cursor AttributesOracle allows you to access information about the most recently executed implicit cursor
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. For more information about this behavior, see the "Cursor Attributes for DML Operations" section in Chapter 14. You can also run the query_implicit_attributes.sql script on the book's web site to test out these values yourself. Let's make sure we understand the implications of this last point. Consider the following two programs:
No matter how many rows of data are in the book table, we will always see "0" displayed in the output window. Because I call remove_from_circulation after my SELECT INTO statement, the SQL%ROWCOUNT reflects the outcome of my silly, impossible DELETE statement, and not the query. If you want to make certain that you are checking the values for the right SQL statement, you should save attribute values to local variables immediately after execution of the SQL statement. I demonstrate this technique in the following example:
|
Saturday, October 31, 2009
Section 15.2. Working with Implicit Cursors
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment