Tải bản đầy đủ (.pdf) (10 trang)

Oracle SQL Plus The Definitive Guide- P31 pot

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (100.96 KB, 10 trang )

< previous page page_270 next page >
Page 270
to doing this. First, set up some COLUMN commands with NEW_VALUE clauses. You need one of these COLUMN
commands for each distinct value in your input string. In keeping with the owner.table example, the following two
commands could be used:
COLUMN owner_name NOPRINT NEW_VALUE s_owner_name
COLUMN table_name NOPRINT NEW_VALUE s_table_name
Next, you need to execute a query that returns the results you want. In this case, the query needs to return the owner
name and table name as separate columns. Be sure to use column aliases to name these columns, and be sure those
aliases match the names used in the COLUMN commands. The following SELECT takes a string in the form owner.
table, and returns two separate values. If the owner is not specified, the name of the current user is returned instead.
SELECT
DECODE(INSTR(&&1,.),
0,USER, /*Default to current user.*/
UPPER(SUBSTR(&&1,1,INSTR(&&1,.)-1))) owner_name,
DECODE(INSTR(&&1,.),
0,UPPER(&&1), /*Only the table name was passed in.*/
UPPER(SUBSTR(&&1,INSTR(&&1,.)+1))) table_name
FROM dual;
Once the query has been executed, the substitution variables named in the COLUMN commands will hold the values
returned by the SELECT. These substitution variables may now be used in the remainder of the script. The following is
a rewrite of the previous SELECT using these variables:
SELECT SUM(bytes)
FROM dba_extents
WHERE segment_name &&s_table_name
AND owner = &&s_owner_name;
By using this technique, you have one point of change that controls how the input is parsed. If there's a bug in your
logic, you only need to fix it in one place. The readability of your script is greatly increased, too. Your scripts will also
be more clearly understood by others, and probably by yourself as well.
Error Handling
SQL*Plus doesn't offer too much in the way of error handling. By default, SQL*Plus simply ignores errors and goes on


to execute either the next command you type in, or the next command in the script you are running. For interactive use,
this is good enough. If an error occurs, you will see the message and take appropriate action. However, the situation is
different when you are running a script. Depending on what the script is doing, you may not want SQL*Plus to blindly
proceed to the next command when an error occurs. Consider the following script, which creates a new table, copies
data to it, then deletes the original table:

< previous page page_270 next page >
< previous page page_271 next page >
Page 271
CREATE TABLE employees AS
SELECT * FROM employee;
DROP TABLE employee;
If the CREATE TABLE command failed, you certainly wouldn't want the script to continue, because you would lose all
your data! To help with this type of situation, SQL*Plus provides the WHENEVER command.
The Whenever Command
With the WHENEVER command, you can give SQL*Plus instructions on what to do when an error occurs. Your
choices are a bit limited: you can either continue when an error occurs, or you can exit SQL*Plus entirely, possibly
returning an error code. Returning an error code is useful if you are calling SQL*Plus from a Unix shell script or a DOS
batch file.
There are two types of errors you can handle with WHENEVER. Each has its own variation of the command.
WHENEVER SQLERROR is used to handle SQL errors and errors raised from PL/SQL blocks. WHENEVER
OSERROR is used to handle operating system errors, such as those you might get when you run out of disk space while
spooling a large data extract.
There is one type of error you cannot detect: an error involving a SQL*Plus command. An example would be if you
were to misspell a command, such as COLUMN. If your script contained a command like the following:
COLUM employee_name HEADEEN Employee Name FLOORMAT A40
SQL*PLus would generate an error and continue on with the script as if nothing had happened. This isn't as much of a
problem as you might first think. You should test your scripts to be sure your SQL*Plus commands are correct. This is
easy to do, and the consequences of a failed SQL*Plus command are usually no worse than some messy formatting of
the output. SQL commands, on the other hand, can fail for a variety of reasons that don't involve simple misspellings. A

simple database change can easily cause a SQL command that worked one day to fail the next. Similarly, with operating-
system errors, you don't know in advance when you will run out of disk space.
WHENEVER SQLERROR
The WHENEVER SQLERROR command tells SQL*Plus what to do when a SQL statement or PL/SQL block fails to
execute properly. The simplest way to use it is to issue the following command, which tells SQL*Plus to simply abort
the script when an error occurs.
WHENEVER SQLERROR EXIT

< previous page page_271 next page >
< previous page page_272 next page >
Page 272
Now, when a SQL error occurs, SQL*Plus will exit, as the following example shows:
SQL> WHENEVER SQLERROR EXIT
SQL> SELECT kristi FROM dual;
SELECT kristi FROM dual
*
ERROR at line 1:
ORA-00904: invalid column name

Disconnected from Oracle7 Server Release 7.3.3.0.0 - Production Release
PL/SQL Release 2.3.3.0.0 - Production
$
When SQL*Plus exits like this, the default behavior is to commit any transaction that might be open. For a simple
SELECT statement like that shown in the previous example, this is not a problem. When you are changing records, it
might be. If your script executes several SQL statements that change data, you may not want to commit unless all the
changes can be made. In this situation, use the ROLLBACK option to tell SQL*Plus to roll back when an error occurs,
like this:
WHENEVER SQLERROR EXIT ROLLBACK
If you're calling SQL*Plus from a Unix shell script, DOS batch file, VMS command file, or the equivalent, you can
have it pass back a return code so your shell script can tell whether your script executed successfully or not. The

following command tells SQL*Plus to pass back a standard failure code when an error occurs:
WHENEVER SQLERROR EXIT FAILURE ROLLBACK
The precise code that gets passed back will vary from one operating system to the next. If a simple success/fail
indication is not enough, you can have SQL*Plus pass back the specific Oracle error code or any other value you want.
The following example shows how to pass back the Oracle error code when a SQL error occurs:
WHENEVER SQLERROR EXIT SQL.SQLCODE
You could also choose to return any arbitrary number, the value of a numeric bind variable, or the value of a
substitution variable.
The default behavior of WHENEVER SQLERROR EXIT is to
COMMIT any pending transaction. You may want to use the ROLLBACK
option to change that behavior.
Using WHENEVER SQLERROR EXIT with the GUI version of SQL*Plus can
be really annoying at times. Any error results in SQL*Plus terminating, causing
the GUI window to close. Usually this happens before you even realize that an
error occurred, making you miss any error message that may have been
displayed.
Don't use THEN. It's WHENEVER SQLERROR EXIT, not WHENEVER
SQLERROR THEN EXIT. I tend to get mixed up on this point often.

< previous page page_272 next page >

< previous page page_273 next page >
Page 273
PL/SQL errors and WHENEVER
The WHENEVER SQLERROR EXIT command will catch any errors in a PL/SQL block, but only if those errors are
raised back to the SQL*Plus level. PL/SQL has its own error-handling mechanism, and using it can prevent SQL*Plus
from knowing that an error ever occurred.
The following PL/SQL block does not contain an error handler, so any SQL errors will be implicitly raised to the calling
routine, which in this case is SQL*Plus:
BEGIN

UPDATE employee
SET employee_billing_rate = employee_billing_rate * 1.10;
COMMIT;
END;
/
However, you could rewrite the block so that it includes an error handler. In that case, the PL/SQL error handler would
get the error, and SQL*Plus would not know about it. Here is the same block, but with an error handler:
DECLARE
success_flag BOOLEAN;
BEGIN
BEGIN
UPDATE employee
SET employee_billing_rate = employee_billing_rate * 1.10;
success_flag := TRUE;
EXCEPTION
WHEN OTHERS THEN
success_flag := false;
END;

IF success_flag THEN
COMMIT;
ELSE
ROLLBACK;
DBMS_OUTPUT.PUT_LINE(The UPDATE failed.);
END IF;
END;
/
In this example, the UPDATE statement is contained in its own PL/SQL block, and any error related to that statement
will be trapped by the exception handler for that block. Even if an error occurs, as far as SQL*Plus is concerned, this
block will have executed successfully. If you want to handle an error within PL/SQL, but still abort the SQL*Plus

script, you can use the RAISE_APPLICATION_ERROR procedure. This procedure is part of a PL/SQL package named
DBMS_STANDARD, and should exist in all installations. You call it like this:
RAISE_APPLICATION_ERROR (error_code, error_message);

< previous page page_273 next page >
< previous page page_274 next page >
Page 274
where:
error_code
Is a negative number. The range from -20000 to -20999 is reserved for userdefined errors.
error_message
Is a text message of up to 2048 characters.
When you call RAISE_APPLICATION_ERROR from a PL/SQL block, control immediately returns to the calling
block. You must call the procedure from the outermost PL/SQL block in order to return the error to SQL*Plus. When
that happens, SQL*Plus will print the error message and then take whatever action you specified in the most recent
WHENEVER SQLERROR command. The next PL/SQL block is the same as the previous one, except for the addition
of the RAISE_APPLICATION_ERROR command, which is used to notify SQL*Plus of an error:
DECLARE
success_flag BOOLEAN;
BEGIN
BEGIN
UPDATE employee
SET employee_billing_rate = employee_billing_rate * 1.10;
success_flag := TRUE;
EXCEPTION
WHEN OTHERS THEN
success_flag := false;
END;

IF success_flag THEN

COMMIT;
ELSE
ROLLBACK;
DBMS_OUTPUT.PUT_LINE(The UPDATE failed.);
RAISE_APPLICATION_ERROR (-20000,
The UPDATE of employee billing rates failed.);
END IF;
END;
/
Now if an error occurs, SQL*Plus will know about it and can abort the script.
WHENEVER OSERROR
The WHENEVER OSERROR command tells SQL*Plus what to do when an operating system error occurs. Running
out of disk space would be a likely operating-system error, one that you might encounter when spooling large amounts
of output from a SQL query.

< previous page page_274 next page >
< previous page page_275 next page >
Page 275
WHENEVER OSERROR works pretty much the same as the WHENEVER SQLERROR command. The simple
version, which just causes SQL*Plus to exit when an error occurs, looks like this:
WHENEVER OSERROR EXIT
By default, any changes are committed when SQL*Plus exits. You can change that behavior using the ROLLBACK
keyword as follows:
WHENEVER OSERROR EXIT ROLLBACK
As with WHENEVER SQLERROR, you can pass a return code back to a shell script in order to allow it to detect the
error. For example:
WHENEVER OSERROR EXIT FAILURE
Unlike the SQLERROR version of the command, there is no equivalent to SQL.SQL-CODE for operating-system
errors. The other options still apply, however, and you can return an arbitrary value, the value from a bind variable, or
the value of a substitution variable.

Be aware that the range of return codes you can pass back from
SQL*Plus varies from one operating system to the next. Under Unix, return
codes are limited to one byte, giving you a range of 0 to 255 to work with.
VMS, on the other hand, allows much larger values to be returned. Keep this in
mind if you are writing a script that needs to be portable across different
operating systems.
The following example archives any project_hours data over one year old to a file, then deletes the data that was
archived. In order to be sure no data is deleted without first being successfully archived, the WHENEVER commands
are used to abort the script in the event of an error.
WHENEVER SQLERROR EXIT FAILURE
WHENEVER OSERROR EXIT FAILURE

Turn off page headings, column titles, etc.
SET PAGESIZE 0
SET HEADING OFF
SET COLSEP ,

Archive old records
SPOOL project_hours_archive.txt
SELECT *
FROM project_hours
WHERE TRUNC(time_log_date) <= ADD_MONTHS(TRUNC(SySDATE),-12);
SPOOL OFF

< previous page page_275 next page >
< previous page page_276 next page >
Page 276
Delete the records just archived
DELETE
FROM project_hours

WHERE TRUNC(time_log_date) <= ADD_MONTHS (TRUNC (SYSDATE),-12);

< previous page page_276 next page >
< previous page page_277 next page >
Page 277
8
Tuning and Timing
In this chapter:
Using SQL*Plus Timers
Using EXPLAIN PLAN
Using AUTOTRACE
Improving on EXPLAIN PLAN Results
Where to Find more Tuning Information

Oracle offers two features that can be used from SQL*Plus to monitor and improve the performance of your scripts and
SQL statements. These two features are SQL*Plus timers and the EXPLAIN PLAN command.
SQL*Plus has a timing feature built into it that can be used to monitor the length of time it takes to execute a SQL
command, a PL/SQL block, or any other part of a script. To measure the time it takes to execute a SQL statement, you
start a timer prior to executing the statement, then display the value of the timer immediately after the statement is
executed.
The EXPLAIN PLAN command, although it is a SQL command, not a SQL*Plus command, is often used from
SQL*Plus. EXPLAIN PLAN can be used to find out exactly how Oracle intends to execute any given SQL query. It
will tell you, for example, whether or not an index will be used, and what the name of that index will be. Once you
know how Oracle intends to execute the query, you can use hints to influence or alter Oracle's default plan based on
your knowledge of the data. A hint is a command to the optimizer that is embedded in a comment within a SQL query.
The optimizer is the part of Oracle that determines how best to retrieve the data required by a SQL statement.
This chapter is not intended to be an exhaustive reference for tuning SQL statements. Several good books have been
written on this subject. What this chapter does provide is a quick overview of the mechanics of tuning and a convenient
summary of the hints available to you.
Using SQL*Plus Timers

SQL*Plus has a crude timing facility built into it that allows you to measure the elapsed time of a script or any portion
of a script. You can even have SQL*Plus

< previous page page_277 next page >
< previous page page_278 next page >
Page 278
report the elapsed execution time of every SQL query and PL/SQL block automatically after each statement has
executed. Timers can be nested so that you can time the overall execution of a script as well as the execution time of
each individual statement.
Timer resolution varies with the hardware platform being used. Under Windows NT or 95, the elapsed time is reported
in milliseconds. Under HP Unix, it is resolved only to the hundredth of a second, but the display format is much nicer.
Here is how an elapsed time of 90.5 seconds will be shown under Windows:
real: 90500
90,500 milliseconds is equivalent to 90.5 seconds. With Unix versions of SQL*Plus, the display is much more user-
friendly. Here's how the HP-UX version of SQL*Plus would report the same value:
Elapsed: 00:01:30.5
Unix uses an hour, minute, second, and hundredths format, so 90.5 seconds is reported as 1 minute, 30 seconds, and 50
hundredths. Remember, the timer is a SQL*Plus timer. The hardware that matters is the machine running the SQL*Plus
executable, not the one running the Oracle server.
Timings can be useful in spotting trends. It may be helpful to know, for example, if a script is taking longer and longer
to run. Timings can also be helpful in comparing the relative efficiency of two SQL statements. If you have two
statements that return equivalent results, and one consistently runs faster than the other, that's the one you probably want
to go with.
Take timing with a grain of salt, though. The timer measures elapsed time, not CPU time, and many factors can throw it
off. The network throughput may vary between the execution of two queries. The load on the server could vary as well.
For example, one query might run more slowly than another simply because many other users all happened to hit the
database at the same time. Be skeptical of once-off results. Look for consistency over several timings.
The SET TIMING Command
You can have SQL*Plus automatically report the elapsed time it takes to execute every query by using the SET
TIMING ON command. For example:

SQL> SET TIMING ON
Now, whenever you execute a query, SQL*Plus will report the elapsed time. With the Windows versions of SQL*Plus,
this time will be reported in milliseconds. The following example shows that it took 110 milliseconds, or about 1/10 of a
second, for a query on DBA_VIEWS to complete:

< previous page page_278 next page >

×