You are on page 1of 16

Oracle

Solutions for High-End


Oracle® DBAs and Developers Professional

Method 4 Dynamic
SQL with Native
Dynamic SQL
Steven Feuerstein
In this article, Steven Feuerstein explores Method 4 Dynamic SQL and its uses.

I
T’S so easy in our chosen professional careers to fall into a veritable
perdition of acronyms and jargon. Some of my non-tekkie friends regularly
castigate me for turning every concept or name into three-letter shorthand.
Why do I do this? Why do technoids around the world do this? Probably
because we live in a highly abstracted world that’s not very easily explained
to anyone, including those who dreamed up the world to begin with. So Sample Issue
rather than figure out how to explain our thinking clearly and simply, we use
“brute force” to convince others that 1) we know what we’re talking about, 1 Method 4 Dynamic SQL
and 2) they should obviously understand us, else there’s something wrong with Native Dynamic SQL
with them. Steven Feuerstein
Inexcusable behavior, any way you look at it. Let me give you an example.
8 Transaction, Heal Thyself!
Here’s one way to describe the article you’re about to read: Part 2
Darryl Hurley
In this article, I will describe the characteristics of Method 4 Dynamic SQL
11 Oracle9i R2 Buffer
(M4) and explore the challenges presented when attempting to implement
Cache Advisory
an M4 statement following normal procedures with Native Dynamic James F. Koopmann
SQL (NDS). I will then introduce dynamic PL/SQL and demonstrate an
NDS-based solution to the M4 problem that at times involves two levels of 16 Downloads
dynamic statement evaluation.

If you ever read gobbledygook like that, you should never say, “Wow, that
person sure is smart.” You should instead say: “I really wonder how well that
Indicates accompanying files are available online at
Continues on page 3 www.oracleprofessionalnewsletter.com.
D:
GE
A
PA
L
FUL QUEST

2 Oracle Professional Sample Issue www.oracleprofessionalnewsletter.com


Method 4 Dynamic SQL... A quick glossary of concepts
I’ve published several articles over the years on dynamic
Continued from page 1 SQL, so I don’t think it would be terribly fair to long-
person understands what he’s writing about. Surely a standing readers (and the publishers) to spend lots of
deep-seated comfort with the material would allow him pages rehashing previously published material. I do,
to express himself more clearly.” however, want to make sure that we have a common base
So allow me to rephrase my description of this article: of knowledge. Table 1 defines some common terms.
Note: If dynamic SQL is entirely new territory for
Did you ever have a situation in which you needed to you, I suggest you buy two of my books: Oracle PL/SQL
put together your SQL query “on the fly,” based on Programming, 3rd edition, which contains a chapter on
information provided by the user at runtime? Sure, Native Dynamic SQL, and Oracle Built-in Packages, which
that’s what “dynamic SQL” is all about. But what if offers a wealth of information on dynamic SQL using the
that statement is extremely dynamic, such as when DBMS_SQL built-in package. I also encourage you to
you don’t even know the number of (or which) check out Quest’s Knowledge Expert for PL/SQL, a
columns will be queried? That’s what’s known as powerful online library containing the contents of my
“Method 4” dynamic SQL, and it can be really tough books and more.
to write the code for such highly variable statements. So I hereby assume for the remainder of the article
Sometimes, it even becomes necessary to forget about that you have a working knowledge of Native Dynamic
writing a dynamic SQL statement and, instead, switch SQL. This means, for example, that you’re familiar with
to constructing an entire PL/SQL block of code on the the EXECUTE IMMEDIATE statement, as well as its INTO
fly. You’ll no doubt find this prospect intimidating at and USING clauses.
first, but once you understand the basics, you might
even find the experience liberating. Let’s solve a Method 4 problem
Let’s get concrete about Method 4 dynamic SQL and the
Hopefully that makes a bit more sense. And with that, kind of code required to write it. Here’s a simple Method
let’s begin the exploration. 4 scenario:

Table 1. A short glossary of terms.

Term Explanation
Dynamic SQL SQL statements, including DDL, DML, and queries, whose logic and syntax aren’t determined until your program is
executed. Usually, this means that the user of the program provides the final “bits” of information needed to complete
the SQL. statement.

Bind variable A value that’s provided to the dynamic SQL statement when executed. In general, the PL/SQL engine replaces a
“placeholder” in the dynamic SQL string with the bind variable or specific value.

Method 1 Dynamic SQL A dynamically constructed SQL statement that isn’t a query and doesn’t contain any “bind variables.” Examples: any
DDL statement or an INSERT, UPDATE, or DELETE without any bind variables.

Method 2 Dynamic SQL A dynamically constructed SQL statement that isn’t a query and contains a fixed number of bind variables. This means
that when you write your code, you know that the user might provide one or even six values, but they’ll always provide
the same number of values, in specific places in the SQL statement.

Method 3 Dynamic SQL A dynamically constructed SELECT statement with a fixed number of columns in the select list and a fixed number of
bind variables. The name of the table being queried might vary, but the query will always return one or three or 25 values,
and the WHERE clause will always contain two or seven or 15 placeholders that require bind variables.

Method 4 Dynamic SQL A dynamically constructed SQL statement in which, essentially, anything goes. On Monday, you might need to return six
values in the select list, without needing any bind variables. On Tuesday, you return just three values, but you have two
bind variables. Or you might have an UPDATE statement.

Dynamic PL/SQL A block of code that’s constructed, compiled, and executed at runtime. This isn’t the same thing as constructing and
executing a “CREATE OR REPLACE PROCEDURE” statement. That’s dynamic DDL. Dynamic PL/SQL occurs when you
construct a “BEGIN...END;” block and run that.

DBMS_SQL The built-in or supplied package that has since PL/SQL 7.1 given us the ability to execute dynamically constructed SQL
statements and PL/SQL blocks. It’s one of the slowest and most complex of the supplied packages.

Native Dynamic SQL Native statements in the PL/SQL language (EXECUTE IMMEDIATE and OPEN FOR) that allow you, in Oracle8i and above, to
execute dynamically constructed SQL statements and PL/SQL blocks—without relying on DBMS_SQL. Native Dynamic
SQL is faster and much, much easier to write than DBMS_SQL. Thanks, Oracle!

www.oracleprofessionalnewsletter.com Oracle Professional Sample Issue 3


I’ve been asked by my manager to write a program each different table, I’d need a record defined with a
(which I’ll call “intab”) that, in essence, performs different %ROWTYPE declaration. That’s not something I
a “SELECT * FROM <table>” entirely within can do dynamically. Or can I?
PL/SQL, revealing what’s in the table. That is, you
provide me with the name of a table, as well as an The solution with DBMS_SQL
optional WHERE clause, and I display the contents Before presenting a solution to this vexing problem based
of the resulting query on the screen, using in Native Dynamic SQL, I’d like to consider how one
DBMS_OUTPUT. might tackle it using good old DBMS_SQL. It turns out,
oddly enough, that DBMS_SQL is more naturally suited
This is a “Method 4 requirement” because, clearly, in some ways to handling Method 4 requirements than
different tables could and usually would have different Native Dynamic SQL.
numbers of columns. If you ask to see the contents of the I wonder if anyone reading this article recalls that
employee table, I’ll need to retrieve the employee name, back in 1995, I published an article in Oracle Professional
department ID, salary, date of birth, date of hire, and so demonstrating how to solve this problem using
on. If you ask to see the contents of the job title table, I’ll DBMS_SQL. I wonder further if any of you actually kept a
only need to grab the ID, name, and description. copy of the newsletter. If you do and did, dig it out and
This variability presents us with a challenge when read my biography at the end of the article. If you send
working with Native Dynamic SQL. The reason is very me a note (steven@stevenfeuerstein.com) telling me the
simple. Here’s the structure of the statement I’d have to name of my employer and my job title at the time, I’ll
use to satisfy my manager (for retrieval of a single row, send you a free copy of the new, second edition of the
in any case): Oracle PL/SQL Language Quick Reference!
The approach I took with DBMS_SQL (described in
EXECUTE IMMEDIATE the aforementioned article and included in intab.sp, in the
'SELECT ' ||
column_list_for_table || Download file at www.oracleprofessionalnewsletter.com)
' FROM ' || table_name ||
' WHERE ' || optional_where_clause
is described at a high level in Figure 1. I first query the
INTO variable1, information about the columns of the table from
variable2,
... ALL_TAB_COLUMNS (saving the details in a set of
variableN; collections). I use that data to build my variable select list,
and then parse the resulting query. Before I can execute
or: the query, I must define each column to DBMS_SQL,
which I do with the DEFINE_COLUMN procedure. Then
EXECUTE IMMEDIATE
'SELECT ' || I execute the query. As for retrieval of data (the sticky part
column_list_for_table ||
' FROM ' || table_name ||
for Native Dynamic SQL), the very granular nature of
' WHERE ' || DBMS_SQL makes this straightforward, if tedious. I
optional_where_clause
INTO record; simply execute a call to DBMS_SQL.COLUMN_VALUE,
retrieving that column’s value.
The construction of the SELECT statement is, itself, In other words, the widely cursed downside of
straightforward enough. I simply concatenate together DBMS_SQL—the need to code each phase of the SQL
all the dynamic pieces that I need. The real problem lies processing—becomes a benefit for Method 4, as it
with the INTO clause. INTO is the part of the EXECUTE literally requires me to explicitly address each column in
IMMEDIATE statement that acts as a kind of bridge
between the SQL engine and my PL/SQL program. It
allows me to take each of the values in the select list of the
query and deposit or fetch them into program variables. I
could fetch into a record or a list of variables.
Regardless of the structure of my local variables, the
INTO clause isn’t dynamic. I don’t construct it at runtime.
Its format is fixed or static from the moment I compile the
program. Clearly, this presents a big problem. If the
number of values identified by the table changes, but my
INTO list doesn’t change, I’ll have a runtime mismatch
and Oracle will raise an exception.
If I fetch into a record, I only have a single variable in
the INTO clause, regardless of the table. That doesn’t Figure 1. A pseudo-code description of the intab procedure—
really solve the essential problem, unfortunately. With DBMS_SQL.

4 Oracle Professional Sample Issue www.oracleprofessionalnewsletter.com


the select list. 5 FROM ALL_TAB_COLUMNS...)
6 LOOP
So much for the zany world of DBMS_SQL. If you 7 construct_select_list;
8 construct_variable_list;
want to live in the present, not the past, you’ll want to 9 END LOOP;
spend your time honing your Native Dynamic SQL skills. 10
11 my_block :=
So how can we get around the problem of a static INTO 12 'BEGIN
13 EXECUTE IMMEDIATE
clause and implement a Method 4 query? 14 ''SELECT '
15 || column_list_for_table
16 || ' FROM '
Paradigm change: Dynamic PL/SQL! 17 || table_name
I need a dynamic INTO clause, regardless of the approach 18 || ' WHERE '
19 || optional_where_clause || '''' ||
I take to querying data, of which there are three options: 20 ' INTO '
21 || list_of_variables
• Use an EXECUTE IMMEDIATE to fetch a single row 22 ||
at a time (shown earlier). 23 'END;';
24
• Use an EXECUTE IMMEDIATE with a BULK 25 EXECUTE IMMEDIATE my_block;
COLLECT INTO clause to fetch multiple, perhaps all, 26* END;

rows in a single statement execution (available in


If this is the first time you’ve ever laid your eyes on
Oracle9i and above).
dynamic PL/SQL, it’s okay to admit at this time that you
• Use the OPEN FOR statement to open my dynamic
feel a bit of a headache coming on. This is mind-bending
query, and then use the traditional FETCH INTO
stuff, and it certainly takes some getting used to.
statement to retrieve the next row.

None of these natively supports dynamic


Rules for dynamic PL/SQL
Perhaps it will help if I review for you some of the rules
construction. So either I give up or I step back and try
that apply to dynamic PL/SQL. Here are three of the most
to come up with a different approach. I need something
important points to keep in mind:
like this:
1. The dynamic string must be a valid PL/SQL block. It
EXECUTE IMMEDIATE must start with the DECLARE or BEGIN keyword,
'SELECT ' ||
column_list_for_table ||
and end with an END statement and a semicolon. The
' FROM ' || table_name || string won’t be considered PL/SQL code unless it
' WHERE ' || optional_where_clause
INTO || list_of_variables; ends with a semicolon.
2. In your dynamic block, you can only access PL/SQL
That construction is, sadly, unrecognized by PL/SQL. code elements that have global scope (standalone
But what about something like that shown in Listing 1? functions and procedures, and elements defined in
Table 2 gives an explanation of my code. the specification of a package). Dynamic PL/SQL
blocks execute outside the scope of the local
enclosing block.
Listing 1. A first stab at using Native Dynamic SQL for Method 4.
3. Errors raised within a dynamic PL/SQL block
1 BEGIN can be trapped and handled by the local block in
2
3 FOR rec IN
which the string was run with the EXECUTE
4 (SELECT * FROM IMMEDIATE statement.

Table 2. Breakdown of Listing 1.

Line(s) Description
3-9 I execute a cursor FOR loop against the ALL_TAB_COLUMNS, identifying just those columns relevant to the specified table (details not
shown). Using that data, I construct the comma-delimited select list and also a comma-delimited variable list. Note: I don’t show the
naming convention for the variables here, and almost any will do; just make sure that there’s a unique variable for each column.

11-23 I construct and assign a string to the variable my_block. Look closely. I’m putting together a valid PL/SQL block, starting with BEGIN
and ending with, well, END;. I’ve stuffed the EXECUTE IMMEDIATE of the SELECT inside this block. The next few entries explain some
of the nuances.

14 The two single quotes before the keyword SELECT evaluate to one quote when the block is compiled. This quote initiates the dynamic
query inside the EXECUTE IMMEDIATE.

19 I terminate the dynamic query inside the dynamic PL/SQL block with the concatenation of four single quotes before the INTO keyword.
Four single quotes evaluate down to one single quote.

25 I execute the dynamic PL/SQL block. When this line is run, the block is constructed (most notably, the list of variables is put together) and
then compiled. Assuming there’s no compilation error, PL/SQL then executes the block, which in turn constructs and executes the query,
fetching the data into the specified list of variables.

www.oracleprofessionalnewsletter.com Oracle Professional Sample Issue 5


I don’t have room in this article to delve more EXECUTE IMMEDIATE
''SELECT '
deeply into these rules. Please check out my books or the || column_list_for_table
|| ' FROM '
Quest Knowledge Expert for additional details. I must, || table_name
however, come back to Rule 2. I can’t reference locally || ' WHERE '
|| optional_where_clause
declared variables inside my dynamic (and globally
scoped) PL/SQL block. This is something of a problem,
and now let’s make it more concrete by supplying values
since I’m declaring my variable list inside the program
for each of these variable elements, as shown in Table 3.
that executes the dynamic PL/SQL block. If I can’t
reference local variables, how can I fetch and manipulate
the data from the query? Table 3. Sample values for dynamic query.
I suppose that I could dynamically create a package
(dynamic DDL) that contains in its specification (and is column_list_for_table last_name, salary, hire_date, date_of_birth
table_name employee
therefore global) a set of variables that I’d then reference
optional_where_clause department_id = 107
in the dynamic query. I’d then need to drop this package
when I’m done. With those values, the query that’s constructed and
That’s all very doable, and, in fact, I’ve written executed by the nested EXECUTE IMMEDIATE is this:
utilities that do precisely that. This is, however, very
complicated stuff. I’m ready to wager (if I were a SELECT last_name
gambling sort of man) that many of you didn’t really , salary
, hire_date
follow what I wrote in the previous paragraph. I would, , date_of_birth
FROM employee
of course, feel compelled to explain it better to you, but I WHERE department_id = 107
want to make a different point and seek a different kind
of common understanding instead. I don’t see anything in that query that’s dynamic!
Let’s appreciate what I’ve accomplished so far: I Lo and behold, when I switched to dynamic PL/SQL, I
found a way to circumvent the restriction on static INTO removed all the dynamic aspects from the query executed
clauses by taking a step back from the dynamic SQL and within that block constructed at runtime.
embedding it in a “larger” dynamic PL/SQL block. It turns out that I don’t really need to use EXECUTE
Unfortunately, this path seems to be leading me to very IMMEDIATE within the block’s EXECUTE IMMEDIATE. I
difficult coding challenges (if it’s hard to explain, you can can switch to static SQL within the dynamic block. That
rest assured that it will be hard to program). should simplify things!
I now face a fork in my software development road. I
can allow myself to be totally caught up in the ego trip of Switching to dynamic PL/SQL and static SQL
conquering any challenge, no matter the difficulty, or I With all of this background in mind, let’s take a look at
can question the very wisdom of this road and allow the intab procedure I built using Native Dynamic SQL
myself this simple question: Is there perhaps a simpler (intab9i.sp; it’s available as part of the Download file).
way to get to the same conclusion? The dynamic PL/SQL heart of this utility is shown at a
I suggest that you seek self-awareness regarding your high level in Figure 2.
relationship to your code. It’s impossible, after all, to not I still have a dynamic PL/SQL block: ‘BEGIN ...
feel personally connected. It’s the product of your brain; END;’ within an EXECUTE IMMEDIATE. But I’ve
it’s in some fundamental ways an extension of yourself. replaced the dynamic SQL execution with a cursor FOR
This doesn’t mean, however, that anything (everything) loop (most tables have more than one row of data to
you’ve written must be right, must be preserved. display). I call the query_string function to construct the
You must remain ready to sacrifice your current
efforts, to question the wisdom of your current design, so
that you might be open to even better ways of doing
things. In the case of my Method 4 challenge, I now force
myself to ask: Do I really need all that dynamic stuff?

Do I really need all that dynamic stuff?


Hmm. That’s an interesting question. Do I really need to
use EXECUTE IMMEDIATE to construct, compile, and
run a PL/SQL block that has within it an EXECUTE
IMMEDIATE of a query? It certainly seemed that way at
first glance. But let’s take a second look at that nested Figure 2. A pseudo-code description of the intab procedure—
dynamic statement: Native Dynamic SQL.

6 Oracle Professional Sample Issue www.oracleprofessionalnewsletter.com


SELECT statement from the ALL_TAB_COLUMNS data. The only limit is your imagination
I call the concatenated_values function to construct a As I said in my debut article at Oracle Technology
big concatenation of all the values retrieved with each Network (“Software as Art: Are You Having Fun Yet?”
iteration of the cursor FOR loop. at http://otn.oracle.com), I feel very strongly that
Gee, that’s so simple! Well, to be very honest with programmers should get an enormous sense of
you, I’ve simply hidden lots of the details involved in satisfaction and joy from their coding efforts. Sure,
constructing the query string and concatenating those software development has engineering aspects to it, but it
values; the intab procedure is 363 lines long, and relies also should be treated as an act of creativity and artistry.
heavily on local procedures and functions to keep the I can’t think of any part of PL/SQL programming
code very readable. As I rewrote my original, DBMS_ that gives me more joy than working with dynamic
SQL-based intab procedure, I also decided to add some PL/SQL. You’re limited only by your imagination. (Okay,
flexibility. The header of the procedure looks like this: plus the syntax of the language and the rules I mentioned
earlier. But rules don’t necessarily constrain. They can
PROCEDURE intab (
table_in IN VARCHAR2
also liberate.)
,string_length_in IN INTEGER := 20 I encourage you to spend some time studying the
,where_in IN VARCHAR2 := NULL
,date_format_in IN VARCHAR2 := 'MM/DD/YY HHMISS' contents of the intab9i.sp file. Make sure you understand
,collike_filter_in IN VARCHAR2 := '%' the various techniques. Then strike out on your own.
,colin_filter_in IN VARCHAR2 := NULL
) Build a nifty utility, solve a thorny problem, and at every
step of the way, have some fun! ▲
In other words, you have some control over
formatting (maximum length of string columns, format FEUER.ZIP at www.oracleprofessionalnewsletter.com
for dates), and you can filter which columns are displayed
either by providing a wildcard for a LIKE comparison or a Steven Feuerstein is considered one of the world’s leading experts on
the Oracle PL/SQL language. He’s the author or co-author of nine books
comma-delimited list of column names.
on PL/SQL, including the now-classic Oracle PL/SQL Programming and
I don’t have room in this article to show you every
Oracle PL/SQL Best Practices (all from O’Reilly & Associates). Steven is a
implementation detail. I encourage you to study the code
Senior Technology Advisor with Quest Software, has been developing
and make sure you understand whichever nuances are software since 1980, and worked for Oracle Corporation from 1987 to
most relevant to your work. 1992. Steven is president of the Board of Directors of the Crossroads
To ensure that you don’t think it’s all smoke and Fund, which makes grants to Chicagoland organizations working for
mirrors, though, I show the implementation of social, racial, and economic justice (www.CrossroadsFund.org).
query_string in Listing 2. Lines 4-19 offer a private steven.feuerstein@quest.com.
function within query_string that
isolates all the logic needed to Listing 2. The query_string function.
construct the WHERE clause
(recognizing that a user of intab 1 FUNCTION query_string
2 RETURN VARCHAR2
might even pass in a GROUP BY or 3 IS
ORDER BY under the guise of a 4 FUNCTION where_clause
5 RETURN VARCHAR2
WHERE clause). 6 IS
Lines 21-27 construct the 7 retval VARCHAR2 (1000) := LTRIM (UPPER (where_in));
8 BEGIN
comma-delimited list of columns 9 IF retval IS NOT NULL
10 THEN
from the collection that was 11 IF (retval NOT LIKE 'GROUP BY%' AND retval NOT LIKE 'ORDER BY%'
populated by the retrieve_ 12 )
13 THEN
column_information procedure. 14 retval := 'WHERE ' || LTRIM (where_clause, 'WHERE');
15 END IF;
Take a close look at this procedure. 16 END IF;
I use dynamic SQL to construct 17
18 RETURN retval;
the query against ALL_TAB_ 19 END;
COLUMNS. I then use the BULK 20 BEGIN
21 FOR indx IN l_columns.FIRST .. l_columns.LAST
COLLECT INTO form to bring back 22 LOOP
23 col_list :=
all column information in a single 24 col_list
statement execution. 25 || l_columns (indx).column_name || ' col' || indx
26 || ifelse (indx = l_columns.LAST, NULL, ',');
Finally, on lines 29-34, I put 27 END LOOP;
28
together all the variable elements 29 RETURN 'SELECT '
into a single query string and pass 30 || col_list
31 || ' FROM '
that back. Remember: There’s no 32 || table_in
longer anything dynamic about 33 || ' '
34 || where_clause;
this query. 35 END query_string;

www.oracleprofessionalnewsletter.com Oracle Professional Sample Issue 7


Oracle
Professional

Transaction, Heal Thyself! Part 2


Darryl Hurley
Oracle9i introduces a long awaited feature that suspends ERROR at line 1:
ORA-01013: user requested cancel of current operation
certain transactions instead of failing them straight away. In
the May 2003 issue, Darryl Hurley discussed how to augment While technically correct (a user did request that the
resumable statements to automatically resolve storage issues. operation be canceled, it just wasn’t the current user), I’m
In this second article of the series, he focuses on a key area left with no way to determine what actually caused the
of resumable statements—namely, responding to timeouts problem. Should the transaction continue? Should I roll
and terminations. everything back? More information is certainly required.
The deceptiveness of ORA-01013 is depicted even

W
HILE resumable statements are a boon to DBAs further within exception handlers, as shown in Listing 1.
and developers alike, they do make one
application concept more complicated:
exception handling. Consider this overly simplistic Listing 1. Trapping ORA-1013 for timeouts.
exception-handling block:
SQL> BEGIN
2 EXECUTE IMMEDIATE 'CREATE TABLE demo ( col1 NUMBER )';
EXCEPTION 3 EXCEPTION
WHEN OTHERS THEN 4 WHEN OTHERS THEN
IF SQLCODE = -01536 THEN 5 IF SQLCODE = -1013 THEN
DBMS_OUTPUT.PUT_LINE('Space Quota Exceeded!'); 6 DBMS_OUTPUT.PUT_LINE('What should I do?');
ELSE 7 ELSE
DBMS_OUTPUT.PUT_LINE('Something Else!'); 8 RAISE;
END IF; 9 END IF;
END; 10 END;
11 /
What should I do?
If the code raises ORA-1536 (space quota exceeded),
this simplified exception handler will display a mundane PL/SQL procedure successfully completed.

message and allow processing to continue. While hardly


an effective scheme for handling exceptions, it illustrates Timeout
how resumable statements can complicate things. What A much friendlier termination is provided by another
will occur if the block of code enters a suspended state procedure in the DBMS_RESUMABLE package,
that’s terminated or times out? In this case it will be SET_SESSION_TIMEOUT.
“Something Else!”.
DBMS_RESUMABLE.SET_SESSION_TIMEOUT(<session id>,<value>);
Thus, the vital question is what to do when
resumable statements terminate or time out. This The new timeout value takes effect “immediately”
article begins with a discussion of possible options for in the selected session. Setting this value to 1 causes an
managing that and concludes with details of how the almost simultaneous timeout.
Transaction, Heal Thyself (THY) architecture handles The error message provided to “un-resumable”
success and failure. statements is now more forthcoming.

Termination SQL> CREATE TABLE demo ( col1 NUMBER );


Terminating suspended statements deemed beyond CREATE TABLE demo ( col1 NUMBER )
*
repair is done using the ABORT procedure in the ERROR at line 1:
DBMS_RESUMABLE package. ORA-30032: the suspended (resumable) statement has
timed out
ORA-01536: space quota exceeded for tablespace 'USERS'
DBMS_RESUMABLE.ABORT(<session ID>);

Statements halted in this manner receive a rather


deceptive error message: Can’t Timeout “Immediately”
A session timeout value of zero is invalid and will be greeted
SQL> create table demo ( col1 number );
by ORA-24280: invalid input value for parameter TIMEOUT.
create table demo ( col1 number )
*

8 Oracle Professional Sample Issue www.oracleprofessionalnewsletter.com


The tricky part is harvesting details of the error. A ' BEGIN' ||
' RAISE v_exception;' ||
starting point is shown in Listing 2. ' END;';
END;
END IF;
END;
Listing 2. Trapping suspended statement timeout.

SQL> BEGIN
The real exception is indeed re-raised, although
2 EXECUTE IMMEDIATE 'CREATE TABLE demo ( col1 NUMBER )'; some details are lost, such as the tablespace name
3 EXCEPTION
(see Listing 5). But at least a calling program can
4 WHEN OTHERS THEN
5 IF SQLCODE = -30032 THEN determine what went wrong based on the Oracle
6 DBMS_OUTPUT.PUT_LINE('I know what to do?'); error number.
7 ELSE
8 RAISE;
9 END IF;
10 END; Listing 5. The re-raised exception.
11 /
I know what to do?
BEGIN
PL/SQL procedure successfully completed. *
ERROR at line 1:
ORA-01536: space quota exceeded for tablespace ''
The real error can be gleaned by parsing the error ORA-06512: at line 1
ORA-06512: at line 11
stack as demonstrated in Listing 3. ORA-30032: the suspended (resumable) statement has
timed out
ORA-01536: space quota exceeded for tablespace 'USERS'
Listing 3. Exception handler extracting the real error message.
The whole point of this first section has been to
EXCEPTION make it very clear that adding resumable statements to
WHEN OTHERS THEN
IF SQLCODE = -30032 THEN applications requires a lot of thought with regards to
DECLARE exception handling.
v_error_stack VARCHAR2(2000) :=
DBMS_UTILITY.FORMAT_ERROR_STACK; The next section of this article switches gears to deal
BEGIN with failure of another sort in THY—specifically,
v_error_stack := SUBSTR(v_error_stack,
INSTR(v_error_stack,CHR(10))+1,1000); determining whether an attempted fix was successful.
DBMS_OUTPUT.PUT_LINE(v_error_stack);
END;
END IF; Triumph or disappointment?
END; In the first article of this series I assumed all fixes
applied by THY were triumphant in allowing
Now the real error number and message displays: suspended statements to resume. However, plenty
of proverbs and past experiences dissuade me from
ORA-01536: space quota exceeded for tablespace 'USERS'
sticking to this assumption. I think I’ll enhance the
application instead.
But what if the code calling this block is designed
Recall that all sessions registering for resumable
to respond to specific error numbers? An actual
statements find themselves listed in the DBA_
exception throwing ORA-1536 must occur. This can
RESUMABLE view. This view includes a column
be accomplished using Native Dynamic SQL (NDS) to
denoting the current status of the session. Thus, the
re-raise the exception, as revealed in Listing 4.
easiest way to check success or failure is to query its
status in this view. To do this, I need to capture the
Listing 4. Re-raising the real exception. session ID (SID) within the AFTER SUSPEND trigger
and record it in the FIXER_INFO table, as demonstrated
EXCEPTION
in Listing 6.
WHEN OTHERS THEN
IF SQLCODE = -30032 THEN
DECLARE
v_error_stack VARCHAR2(2000) := Listing 6. Gathering session ID (SID) in the AFTER
DBMS_UTILITY.FORMAT_ERROR_STACK;
BEGIN
SUSPEND trigger.
v_error_stack := SUBSTR(v_error_stack,
INSTR(v_error_stack,CHR(10))+1, CURSOR curs_get_sid IS
10000); SELECT sid
v_error_stack := SUBSTR(v_error_stack,4, FROM v$mystat;
INSTR(v_error_stack,':') - 4); v_sid NUMBER;
EXECUTE IMMEDIATE 'DECLARE ' || …
' v_exception EXCEPTION;' || OPEN curs_get_sid;
' PRAGMA EXCEPTION_INIT(v_exception,' || FETCH curs_get_sid INTO v_sid;
v_error_stack || ');' || CLOSE curs_get_sid;

www.oracleprofessionalnewsletter.com Oracle Professional Sample Issue 9


… the session holds all of its locks, temporary tablespace
INSERT INTO fixer_info(fixer_id,
sid, extents, rollback extents, and so on for the whole time
… it’s suspended. This can cause other sessions to wait for
VALUES(fixer_info_seq.nextval,
v_sid, these resources unnecessarily. It’s obviously better to
… terminate the suspended statement as soon as it’s deemed
beyond help.
Later, the FIXER package evaluates success as shown
in Listing 7.
Unabated timeouts
As detailed in the first section of this article, the upside of
Listing 7. Checking fix success (session back to NORMAL). timing out over terminating is better exception handling.
The downside is that the session’s timeout value remains
/*--------------------------------------------------*/ reset, leaving subsequent statements with only one
FUNCTION normal_status ( p_sid NUMBER ) RETURN BOOLEAN IS
/*--------------------------------------------------*/ second to be repaired. Consider this example:
CURSOR curs_check_normal ( cp_sid NUMBER ) IS
SELECT NULL
10:00:00 Session established with timeout of 120 seconds
FROM dba_resumable (two minutes)
WHERE session_id = cp_sid 10:30:00 Session suspended due to space quota exceeded
AND status = 'NORMAL'; situation
10:30:01 Space quota increased by 1MB
v_dummy VARCHAR2(1);
v_ret_val BOOLEAN := FALSE;
10:30:03 Statement remains suspended because 1MB increase
wasn’t enough
BEGIN 10:32:04 Session timeout reset to one second, statement fails
but session continues
OPEN curs_check_normal(p_sid); 10:38:00 Session suspended due to space quota exceeded
FETCH curs_check_normal INTO v_dummy;
v_ret_val := curs_check_normal%FOUND; situation (again)
CLOSE curs_check_normal; 10:38:01 Statement times out

RETURN(v_ret_val);
Even the fastest DBA or automated device requires
END normal_status; more than one second to resolve problems.
A solution is to reset the session’s timeout value
Wasted suspension within exception or error handling routines whenever a
Now that the outcome of a fix can be evaluated within session timeout (ORA-30032) is processed. This requires
THY, it’s time to respond to the outcome—specifically easy access to the original value. One way that can be
when a suspended statement is deemed beyond help. provided is using an application context as created in
Consider this example: Listing 8.

10:00:00 Session established with timeout of 120 seconds


(two minutes) Listing 8. Application context handling session timeout value.
10:30:00 Session suspended due to space quota exceeded
situation CREATE OR REPLACE CONTEXT suspend_context
10:30:01 Space quota increased by 1MB USING suspend_context_pkg;
10:30:03 Statement remains suspended because 1MB increase
wasn’t enough CREATE OR REPLACE PACKAGE suspend_context_pkg AS
10:32:00 Statement failure finally signaled to session
PROCEDURE set_timeout ( p_timeout NUMBER );

Almost two minutes (117 seconds) is wasted END suspend_context_pkg;


/
waiting for a fix that’s just not coming. In the grand
scheme of things that’s not a large loss, but remember— CREATE OR REPLACE PACKAGE BODY suspend_context_pkg AS

/*---------------------------------------------*/
PROCEDURE set_timeout ( p_timeout NUMBER ) AS

Not Immediately Resumed /*---------------------------------------------*/


BEGIN
DBMS_SESSION.SET_CONTEXT('suspend_context',
There will be a lag time between the fix completing and 'TIMEOUT',p_timeout);
END set_timeout;
the suspended statement (and session) actually resuming.
Therefore, a pause is required before evaluating status. END suspend_context_pkg;

My testing revealed that a pause of two seconds (using


The AFTER LOGON trigger calls the SET_TIMEOUT
DBMS_LOCK.SLEEP) sufficed, but I recommend establishing
procedure directly using the appropriate value from the
what’s best in your own environment.
Continues on page 15

10 Oracle Professional Sample Issue www.oracleprofessionalnewsletter.com


Oracle
Professional

Oracle9i R2 Buffer Cache


Advisory
James F. Koopmann
If you want to get the most out of Oracle, you’ll have to learn may get errors. This is because of the way that Oracle
how to size its internal memory structures. In this article, allocates and releases memory in the shared pool for this
James Koopmann explores a new tool that Oracle has given parameter. My advice is to at least have this parameter
us to ponder the question: What can I expect if I change my in the READY state. It only takes about 100 bytes for you
buffer cache size? to be able to use this feature for each of the buffer caches
you are using. So if you were to have all eight buffer

A
S an Oracle DBA, you quickly learn the golden caches defined, which I’m sure you don’t, you’d only take
rule: Disk is slow, memory is fast, and the up 800 bytes.
more information you can keep in memory for Run this query to determine your current setting:
immediate access, the better performance you’ll
experience and less resources you’ll consume. The SQL> SHOW PARAMETER db_cache_advice
NAME TYPE VALUE
complexity comes when determining how much memory -------------------------------- ----------- -----------
you should allocate for Oracle to use. db_cache_advice string ON

What is the buffer cache advisory? To change your current setting, run this:
The buffer cache advisory is an Oracle9i feature that
SQL> ALTER SYSTEM SET db_cache_advice=ON SCOPE=SPFILE;
allows for the gathering of statistics on and the prediction
of using differently sized buffer caches. When this feature
STATISTICS_LEVEL
is turned on, it populates the V$DB_CACHE_ADVICE
Oracle9i has introduced a new initialization parameter
performance view with a row of predicted I/O activity
called STATISTICS_LEVEL. You can use this parameter
information for some 20 sample sizes for each buffer
to set DB_CACHE_ADVICE. Be aware that if you do have
cache in use. This advisory is available for all buffer
a setting in your init.ora or spfile for DB_CACHE_
caches (2K, 4K, 8K, 16K, 32K, KEEP, and RECYCLE
ADVICE already, the STATISTICS_LEVEL parameter
buffer caches).
won’t affect or change this setting. Also note that setting
the STATISTICS_LEVEL will turn on other advisories and
What affects the setting?
statistical collections that you may not want. This
Two Oracle initialization parameters are available to
parameter has three settings and must be set to TYPICAL
configure the advisory—namely, DB_CACHE_ADVICE
or ALL for the DB_CACHE_ADVICE advisory to be
and STATISTICS_LEVEL.
turned on:
• BASIC—Causes no advisories or statistics to
DB_CACHE_ADVICE
be collected.
In order to control the collection of advisory statistics,
• TYPICAL—Causes most of the advisories and
you must set the DB_CACHE_ADVICE parameter to
statistics to be collected.
one of the following values:
• ALL—Causes all of the advisories and statistics
• OFF—Advisory is turned off.
to be collected.
• READY—Advisory is turned off. Previously
collected statistics in V$DB_CACHE_ADVICE
You can check to see whether you have an entry in
are kept intact.
your parameter file if the following SQL returns a value of
• ON—Advisory is turned on.
FALSE in the ISDEFAULT column:
When switched on from any other state, the statistics
SELECT name, value, isdefault FROM v$parameter
in V$DB_CACHE_ADVICE are cleared. The most WHERE NAME ='db_cache_advice';
important thing to realize is that if you start up your
database in the OFF state, or switch to the OFF state, you Run this query to determine your current setting:

www.oracleprofessionalnewsletter.com Oracle Professional Sample Issue 11


SQL> SHOW PARAMETER statistics_level col buffhit1 for 990.0000 head 'Buffer|HitRatio'
NAME TYPE VALUE SELECT b.name, b.block_size,
-------------------------------- ----------- ----------- 100*(1 - ((e.physical_reads-b.physical_reads) /
statistics_level string TYPICAL decode(((e.db_block_gets-e.db_block_gets)+
(e.consistent_gets-b.consistent_gets)),
0,1,null,1,((e.db_block_gets-b.db_block_gets)+
To change your setting, run this: (e.consistent_gets-b.consistent_gets))))) buffhit1
FROM beg_buffer_pool_statistics b,
end_buffer_pool_statistics e
SQL> ALTER SYSTEM SET where b.name=e.name
statistics_level=TYPICAL SCOPE=SPFILE; and b.block_size=e.block_size;

Calculate the buffer cache hit ratio for the system The problem with just using the buffer cache hit ratio
Before the buffer cache advisory became available, the in the past was that most DBAs blindly increased the size
staple of sizing the buffer cache was to use a buffer cache of the buffer cache if the hit ratio was low. They’d do this
hit ratio. This ratio was a percentage of how often a block hoping to get an increase in the hit ratio without fully
that’s been requested was found in the buffer cache and understanding the actual amount of physical I/O they
didn’t require physical I/O. The typical methodology was were trying to alleviate. They also just took a guess about
for a DBA to come up with a ballpark estimate of what the size they should increase the buffer cache to. This is
size he thought it should be and then, if he was a smart where Oracle now helps by giving us a tool that helps us
DBA, run a typical workload through the system, take a systematically adjust the buffer cache sizes by real
look at the buffer cache hit ratio, and then adjust the size statistical data. Read on to see how.
if he thought the hit ratio wasn’t adequate.
Here’s the SQL that will give you the hit ratio for each Use of the V$DB_CACHE_ADVICE view
of the individual buffer caches: Now, alongside the buffer cache hit ratio we have a new
facility, the DB_CACHE_ADVICE view. Before running
--# bchitratio.sql the following SQL, there are a couple of things you
set linesize 132
set pagesize 30 should do to make sure your statistics are valid. The first
col buffhit1 for 990.0000 head 'Buffer|HitRatio' thing you should do is clear out the statistics. You can
SELECT name, block_size,
100*(1 - (physical_reads / accomplish this by setting DB_CACHE_ADVICE to
decode((db_block_gets+consistent_gets),
0,1,null,1,(
READY and then to ON. Then you should run a valid
db_block_gets+consistent_gets)))) buffhit1 workload through your system. Then and only then will
FROM v$buffer_pool_statistics;
the following output be valid. Listing 1 shows the SQL
(db_cache_advice.sql) to look at the buffer cache advice
The flaw with the buffer cache hit ratio in
for all buffers.
bchitratio.sql is that it takes into consideration the
Here’s the output generated:
total number of reads and gets from the time you last
bounced your database to the current point in time. It’s Estd Phys Estd Phys
always best to calculate the hit ratio over a period of NAME BLOCK_SIZE Cache Size (MB) Read Factor Reads
------- ---------- --------------- ----------- ------------
time instead of from database startup. Therefore, in DEFAULT 8192 48 2.59 20,822,942
DEFAULT 8192 96 1.77 14,185,093
order to get a valid hit ratio calculation, you need to DEFAULT 8192 144 1.56 12,540,995
capture the actual statistic values before you run your DEFAULT 8192 192 1.43 11,464,624
DEFAULT 8192 240 1.32 10,621,852
workload through the system and then get the ending DEFAULT 8192 288 1.24 9,943,761
DEFAULT 8192 336 1.16 9,328,107
statistics after the workload is done. Let’s see how we DEFAULT 8192 384 1.11 8,938,872
can accomplish this. DEFAULT 8192 432 1.07 8,605,376
DEFAULT 8192 480 1.04 8,325,580
DEFAULT 8192 528 1.01 8,110,005
Calculate the buffer cache hit ratio for a DEFAULT
DEFAULT
8192
8192
544
576
1.00
.98
8,031,154
7,892,752
given workload DEFAULT 8192 624 .96 7,714,677
Here’s the SQL (workload_
bchitratio.sql) for calculating the Listing 1. The SQL to view the buffer cache advice for all buffers.
buffer cache hit ratio over a period
of time, assuming you have tables --# db_cache_advice.sql
col size_for_estimate for 999999 head 'Cache Size (MB)'
beg_buffer_pool_statistics that has col estd_physical_read_factor for 999.90 head 'Estd Phys|Read Factor'
the beginning statistics and col estd_physical_reads for 999,999,999 head 'Estd Phys| Reads'
end_buffer_pool_statistics that has SELECT name,
the ending statistics: block_size,
size_for_estimate,
estd_physical_read_factor,
--# workload_bchitratio.sql estd_physical_reads
set linesize 132 FROM V$DB_CACHE_ADVICE
set pagesize 30 WHERE advice_status = 'ON';

12 Oracle Professional Sample Issue www.oracleprofessionalnewsletter.com


DEFAULT 8192 672 .94 7,585,026 1. Make sure you have a stabilized environment.
DEFAULT 8192 720 .93 7,468,045
DEFAULT 8192 768 .92 7,364,377 a. Set your DB_CACHE_SIZE.
DEFAULT 8192 816 .91 7,271,890
DEFAULT 8192 864 .89 7,187,201 b. Bounce your database box.
DEFAULT 8192 912 .89 7,112,457 c. Run a valid workload through the system to
DEFAULT 8192 960 .87 7,020,440
populate the buffer cache.
A key data point to keep in touch with is the line 2. Clear the statistics in the V$CACHE_ADVICE view
where the estimated physical read factor is 1.00. This by running following SQL commands:
typically is your current setting for the buffer cache.
Alter system set db_cache_advice=ready;
When determining whether you should alter your Alter system set db_cache_advice=on;
buffer cache size, you need to take a look at the number of
estimated physical reads that you’ll save or give up if you 3. Record the current (beginning) statistic values for
decide to adjust the size of the buffer cache up or down. V$BUFFER_POOL_STATISTICS with this SQL:
Typically, as you increase the size of the buffer cache
you’ll quickly see a diminishing amount of return for the Create table beg_buffer_pool_statistics as select *
from v$buffer_pool_statistics;
size of your new cache size.
There are really only two criteria that you should 4. Run your workload through the system.
be concerned with for determining your cache size. The 5. Record the ending statistic values for
first is obviously the fact that you don’t want to increase V$BUFFER_POOL_STATISTICS:
the size of your cache if you’re not going to reduce the
number of reads for the cost of memory. Secondly and Create table end_buffer_pool_statistics as select *
conversely, you don’t want to reduce the size of your from v$buffer_pool_statistics;

cache if you’re going to incur many more physical reads.


6. Display the real buffer cache hit ratio for the interval
Buffer cache sizing with hit ratio and (workload_bchitratio.sql).
V$DB_CACHE_ADVICE 7. Drop the temporary statistic tables with this SQL:
By putting a valid buffer cache hit ratio and buffer cache
Drop table beg_buffer_pool_statistics;
advice together, we can efficiently size and check the Drop table end_buffer_pool_statistics;
buffer cache statistics. Here’s a simple scenario I put
together to give you a feel for changing the buffer cache 8. Display V$DB_CACHE_ADVICE statistics
size for a workload put through a system. (db_cache_advice.sql).
Here are the steps for this example:
Example for different buffer cache sizes
In this example, I’ve followed the procedures described in
Listing 2. Output of workload_bchitratio.sql after setting the
the previous section to set the buffer cache to 16MB, and
buffer cache to 16MB.
collected the results shown in Listing 2 and Listing 3.
Buffer I noticed that even though I was getting a high
NAME BLOCK_SIZE HitRatio buffer cache hit ratio (98.5 percent), the number of reads I
-------------------- ---------- ---------
DEFAULT 8192 98.5022 could save reduced the number of physical I/O by almost
half if I increased the buffer cache to
96MB. This seemed like a small
Listing 3. Output of db_cache_advice.sql after setting the buffer cache to 16MB.
amount of memory for my system
Estd Phys Estd Phys to give up, so I tried it. You can see
NAME BLOCK_SIZE Cache Size (MB) Read Factor Reads the results in Listing 4 and Listing 5
-------------------- ---------- --------------- ----------- ------------
DEFAULT 8192 16 1.00 85,085 (on page 14).
DEFAULT 8192 32 .71 60,771 The results revealed that I
DEFAULT 8192 48 .65 55,300
DEFAULT 8192 64 .59 50,085 was able to maintain a great buffer
DEFAULT 8192 80 .54 45,585
DEFAULT 8192 96 .53 44,716 cache hit ratio (99.7 percent), which
DEFAULT 8192 112 .52 44,486 I expected. What I didn’t expect
DEFAULT 8192 128 .52 44,307
DEFAULT 8192 144 .52 44,128 was the stabilization of physical
DEFAULT 8192 160 .51 43,591 reads after about 48MB for the
DEFAULT 8192 176 .51 42,977
DEFAULT 8192 192 .51 42,977 buffer cache size. This basically
DEFAULT 8192 208 .51 42,977
DEFAULT 8192 224 .51 42,977 tells me that I need to reduce the
DEFAULT 8192 240 .51 42,977 size of my buffer cache by half
DEFAULT 8192 256 .51 42,977
DEFAULT 8192 272 .51 42,977 and give it back to other system
DEFAULT 8192 288 .51 42,977 processes. So I gave it a try to see
DEFAULT 8192 304 .51 42,977
DEFAULT 8192 320 .51 42,977 what would happen—see Listing 6
www.oracleprofessionalnewsletter.com Oracle Professional Sample Issue 13
and Listing 7 for the results. KOOP.ZIP at www.oracleprofessionalnewsletter.com
The results showed that I had maintained a great
buffer cache hit ratio (99.8 percent). I can’t quite explain
James F. Koopmann is co-founder and chief architect of dbDoctor Inc.
the slight increase, but it must be attributable to my (www.dbdoctor.net). He has more than 14 years of database design,
“not so perfect” workload. My workload wasn’t exactly development, and performance tuning experience and specializes in
the same across all tests but was very similar in the Oracle database administration and performance tuning. He’s a featured
type of transactions and data. I figured this would be author for Database Journal, has been published in many database-
a bit more real-world. As you can see, 48MB seems to related publications, and enjoys speaking at local Oracle user groups.
be the “sweet spot” for this system, since decreasing the jkoopmann@dbdoctor.net or james_Koopmann@yahoo.com.
buffer cache size will incur an extra
1,000 physical reads but increasing Listing 4. Output of workload_bchitratio.sql after setting the buffer cache to 96MB.
the size will have no impact on the
physical reads. Buffer
NAME BLOCK_SIZE HitRatio
-------------------- ---------- ---------
DEFAULT 8192 99.7382
Altering the size of the
buffer cache(s)
In my example I shut down the Listing 5. Output of db_cache_advice.sql after setting the buffer cache to 96MB.
database, but this isn’t required to
Estd Phys Estd Phys
change the size of the buffer cache. NAME BLOCK_SIZE Cache Size (MB) Read Factor Reads
You can dynamically change the -------------------- ---------- --------------- ----------- ------------
DEFAULT 8192 16 1.82 30,043
DB_CACHE_SIZE parameter value DEFAULT 8192 32 1.18 19,430
DEFAULT 8192 48 1.00 16,533
with a simple alter system command. DEFAULT 8192 64 1.00 16,533
Reducing the size is simple; just issue DEFAULT 8192 80 1.00 16,533
DEFAULT 8192 96 1.00 16,533
the command for the smaller size. If DEFAULT 8192 112 1.00 16,533
DEFAULT 8192 128 1.00 16,533
you want to increase the size, you DEFAULT 8192 144 1.00 16,533
have two options. The first is to DEFAULT 8192 160 1.00 16,533
DEFAULT 8192 176 1.00 16,533
reduce the size of another cache and DEFAULT 8192 192 1.00 16,533
give that memory to the cache you DEFAULT 8192 208 1.00 16,533
DEFAULT 8192 224 1.00 16,533
wish to increase. Your second option DEFAULT 8192 240 1.00 16,533
DEFAULT 8192 256 1.00 16,533
is to increase the size of your cache DEFAULT 8192 272 1.00 16,533
up to the amount of your SGA_ DEFAULT 8192 288 1.00 16,533
DEFAULT 8192 304 1.00 16,533
MAX_SIZE parameter. Personally, DEFAULT 8192 320 1.00 16,533
I like the latter method since I
don’t have to shut down my instance, Listing 6. Output of workload_bchitratio.sql after setting the buffer cache to 48MB.
and in a 24/7 world I hope you’d
also take this into consideration. Buffer
NAME BLOCK_SIZE HitRatio
Setting this variable doesn’t take -------------------- ---------- ---------
DEFAULT 8192 99.8493
up memory. It just allows you to
dynamically allocate more to your
caches. You can see if you have any Listing 7. Output of db_cache_advice.sql after setting the buffer cache to 48MB.
available free memory to allocate via
Estd Phys Estd Phys
the V$SGA_DYNAMIC_FREE_ NAME BLOCK_SIZE Cache Size (MB) Read Factor Reads
MEMORY view. -------------------- ---------- --------------- ----------- ------------
DEFAULT 8192 16 1.55 15,220
DEFAULT 8192 32 1.11 10,896
DEFAULT 8192 48 1.00 9,839
Conclusion DEFAULT 8192 64 1.00 9,839
Oracle has given us another truly DEFAULT 8192 80 1.00 9,839
DEFAULT 8192 96 1.00 9,839
helpful set of statistics so that we can DEFAULT 8192 112 1.00 9,839
DEFAULT 8192 128 1.00 9,839
become better and more informed DEFAULT 8192 144 1.00 9,839
DBAs in respect to what’s happening DEFAULT 8192 160 1.00 9,839
DEFAULT 8192 176 1.00 9,839
within our databases. Use the old DEFAULT 8192 192 1.00 9,839
methodology of buffer cache hit ratio DEFAULT 8192 208 1.00 9,839
DEFAULT 8192 224 1.00 9,839
alongside the new V$DB_CACHE_ DEFAULT 8192 240 1.00 9,839
DEFAULT 8192 256 1.00 9,839
ADVICE to truly tune the buffer DEFAULT 8192 272 1.00 9,839
cache on real workloads. There’s no DEFAULT 8192 288 1.00 9,839
DEFAULT 8192 304 1.00 9,839
guessing game going on here. ▲ DEFAULT 8192 320 1.00 9,839

14 Oracle Professional Sample Issue www.oracleprofessionalnewsletter.com


Transaction, Heal Thyself... Public access to the context and its underlying
package is required so it can be used in exception and
Continued from page 10 error handling routines. This is provided by a new
procedure in the FIXER package called RESET_TIMEOUT,
RESUMABLE_USER table, as shown in Listing 9. revealed in Listing 10.

Listing 9. AFTER LOGON trigger calling SET_TIMEOUT. Listing 10. RESET_TIMEOUT procedure in FIXER package.

CREATE OR REPLACE TRIGGER logon_trigger /*-------------------------------------------------*/


AFTER LOGON PROCEDURE reset_timeout IS
ON DATABASE /*-------------------------------------------------*/
DECLARE
v_context DBMS_SESSION.APPCTXTABTYP;
-- cursor to get settings for user v_counter INTEGER;
CURSOR curs_get_setting ( cp_user VARCHAR2 ) IS
BEGIN
SELECT *
FROM resumable_user
DBMS_SESSION.LIST_CONTEXT(v_context,v_counter);
WHERE username = cp_user;
v_setting_rec curs_get_setting%ROWTYPE; FOR counter IN 1..v_counter LOOP
BEGIN IF v_context(counter).namespace =
'SUSPEND_CONTEXT' AND
-- check if the user is allowed to use v_context(counter).attribute = 'TIMEOUT' THEN
-- resumable statements
OPEN curs_get_setting(ORA_LOGIN_USER); DBMS_RESUMABLE.SET_TIMEOUT
FETCH curs_get_setting INTO v_setting_rec; (v_context(counter).value);

-- alter the session to enable resumable statements END IF;


-- if the user is allowed to
IF curs_get_setting%FOUND THEN END LOOP;
EXECUTE IMMEDIATE
'ALTER SESSION ENABLE RESUMABLE NAME ' END reset_timeout;
|| '''' || v_setting_rec.name
|| '''' || ' TIMEOUT '
|| v_setting_rec.timeout; Since public access is only required for this one
suspend_context_pkg.set_timeout procedure, I’ll create a standalone calling procedure and
(v_setting_rec.timeout);
END IF; give it a public synonym.

CLOSE curs_get_setting; /*-------------------------------------------------*/


CREATE OR REPLACE PROCEDURE reset_timeout AS
END; /*-------------------------------------------------*/

Don’t miss another issue! Subscribe now and save!


Subscribe to Oracle Professional today and receive a special one-year introductory rate:
Just $209* for 12 issues (that’s $20 off the regular rate)

NAME ❑ Check enclosed (payable to Pinnacle Publishing)


❑ Purchase order (in U.S. and Canada only); mail or fax copy
COMPANY
❑ Bill me later
❑ Credit card: __ VISA __MasterCard __American Express
ADDRESS
CARD NUMBER EXP. DATE

CITY STATE/PROVINCE ZIP/POSTAL CODE


SIGNATURE (REQUIRED FOR CARD ORDERS)

COUNTRY IF OTHER THAN U.S.


Detach and return to:
Pinnacle Publishing ▲ 316 N. Michigan Ave. ▲ Chicago, IL 60601
E-MAIL Or fax to 312-960-4106

* Outside the U.S. add $25. Orders payable in


306INP
PHONE (IN CASE WE HAVE A QUESTION ABOUT YOUR ORDER) U.S. funds drawn on a U.S. or Canadian bank.

Pinnacle, A Division of Lawrence Ragan Communications, Inc. ▲ 800-493-4867 x.4209 or 312-960-4100 ▲ Fax 312-960-4106

www.oracleprofessionalnewsletter.com Oracle Professional Sample Issue 15


BEGIN SPACE QUOTA EXCEEDED procedure and complete
fixer.reset_timeout;
END; the FIXER package (both introduced in the first article),
/
including logging, maximum, and repeated fix attempts.
GRANT EXECUTE ON reset_timeout TO PUBLIC; It will also present the first set of downloadable code
CREATE OR REPLACE PUBLIC SYNONYM reset_timeout
FOR reset_timeout; for the series. ▲

Now exception and error handling code only needs to Darryl Hurley has been working with Oracle technology for 15 years,
call RESET_TIMEOUT to reset the session’s timeout value. with a significant focus on database administration and PL/SQL
development. His days are spent as the database manager for MDSI
WHEN OTHERS THEN Mobile Data Solutions Inc (www.mdsi-advantex.com), and his spare
IF SQLCODE = -30032 THEN
RESET_TIMEOUT; time is spent writing articles and teaching under the moniker of
handle_exception(… ImpleStrat Solutions (www.implestrat.com). He’s written several articles
for the Oracle Development Tools User Group (www.odtug.com) and
Coming up next time contributed to several Oracle books from O’Reilly & Associates
The next article in this series will flush out the complete (www.ora.com). dhurley@mdsi.ca or darryl.hurley@implestrat.com.

Downloads
• FEUER.ZIP—Source code to accompany Steven Feuerstein’s • KOOP.ZIP—Source code to accompany James F.
article, “Method 4 Dynamic SQL with Native Dynamic SQL.” Koopmann’s article, “Oracle9i R2 Buffer Cache Advisory.”

Know a clever shortcut? Have an idea for an article for Oracle Professional?
See below for the contact information where you can send your ideas.

Editor: Garry Chan (gchan@procaseconsulting.com) Oracle Professional (ISSN 1525-1756)


CEO & Publisher: Mark Ragan is published monthly (12 times per year) by:
Group Publisher: Connie Austin Pinnacle
Executive Editor: Farion Grove A division of Lawrence Ragan Communications, Inc.
Production Editor: Andrew McMillan 316 N. Michigan Ave., Suite 400
Chicago, IL 60601

Questions? POSTMASTER: Send address changes to Lawrence Ragan Communications, Inc., 316
N. Michigan Ave., Suite 400, Chicago, IL 60601.
Customer Service:
Phone: 800-493-4867 x.4209 or 312-960-4100 Copyright © 2003 by Lawrence Ragan Communications, Inc. All rights reserved. No part
of this periodical may be used or reproduced in any fashion whatsoever (except in the
Fax: 312-960-4106 case of brief quotations embodied in critical articles and reviews) without the prior
Email: PinPub@Ragan.com written consent of Lawrence Ragan Communications, Inc. Printed in the United States
of America.
Editorial: FarionG@Ragan.com
Oracle, Oracle 8i, Oracle 9i, PL/SQL, and SQL*Plus are trademarks or registered trademarks of
Advertising: HowardF@Ragan.com Oracle Corporation. Other brand and product names are trademarks or registered trademarks
of their respective holders. Oracle Professional is an independent publication not affiliated
Pinnacle Web Site: www.pinnaclepublishing.com with Oracle Corporation. Oracle Corporation is not responsible in any way for the editorial
policy or other contents of the publication.

Subscription rates This publication is intended as a general guide. It covers a highly technical and complex
subject and should not be used for making decisions concerning specific products or
applications. This publication is sold as is, without warranty of any kind, either express or
United States: One year (12 issues): $229; two years (24 issues): $389 implied, respecting the contents of this publication, including but not limited to implied
warranties for the publication, performance, quality, merchantability, or fitness for any particular
Other:* One year: $254; two years: $432 purpose. Lawrence Ragan Communications, Inc., shall not be liable to the purchaser or any
other person or entity with respect to any liability, loss, or damage caused or alleged to be
Single issue rate: caused directly or indirectly by this publication. Articles published in Oracle Professional
$27.50 ($32.50 outside United States)* reflect the views of their authors; they may or may not reflect the view of Lawrence Ragan
Communications, Inc. Inclusion of advertising inserts does not constitute an endorsement by
* Funds must be in U.S. currency. Lawrence Ragan Communications, Inc., or Oracle Professional.

16 Oracle Professional Sample Issue www.oracleprofessionalnewsletter.com

You might also like