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

Oracle SQL Plus The Definitive Guide- P28 ppsx

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 (121.51 KB, 10 trang )

< previous page page_244 next page >
Page 244
Simulating Branching by Adjusting the WHERE Clause.
Suppose you are writing a script to delete all data from the project_hours table. Before you delete the data, you want to
ask the user to confirm the operation. You really want to write something like this:
ACCEPT s_delete_confirm PROMPT Delete project hours data (Y/N)?

IF s_delete_confirm = Y THEN
DELETE
FROM project_hours;
END IF
Of course, you can't do that! SQL*Plus has no IF statement, remember? However, you can add a WHERE clause to the
DELETE statement that will have the same effect. Here's an example:
SET VERIFY OFF

ACCEPT s_delete_confirm PROMPT Delete project hours data (Y/N)?

DELETE
FROM project_hours
WHERE UPPER(&&s_delete_confirm) = Y;
When you execute the script, the DELETE will always be executed. However, if the user answers with an N, the
WHERE clause will always evaluate to FALSE, and no rows will be deleted. Verification is set off only to prevent
SQL*Plus from echoing the line of the WHERE clause that references the substitution variable. The UPPER function is
used in this case in order to allow the user's response to be case-insensitive. Here's how it looks when a user runs this
script and doesn't confirm the delete:
SQL> @delete_hours
Delete project hours data (Y/N)?n

0 rows deleted.
If you wanted to, you could even write an additional query to give the user an error message if the response to the
prompt was not a Y or an N. Adding these lines immediately after the ACCEPT statement would do that for you:


SET HEADING OFF
SET PAGESIZE 0
SET FEEDBACK OFF
SELECT You must answer with a Y or N.
FROM DUAL
WHERE UPPER(&&s_delete_confirm) NOT IN (y,N)
OR &&s_delete_confirm IS NULL;

< previous page page_244 next page >
< previous page page_245 next page >
Page 245
SET FEEDBACK ON
SET PAGESIZE1
SET HEADING ON
To make the results of this query look like an error message, both headings and pagination are turned off. Feedback is
also turned off to avoid giving the 1 row selected message to the user. After the SELECT executes, these settings are
returned to their defaults. Now, here is what happens when you run the modified script and don't answer with a Y or N:
SQL> @delete_hours
Delete project hours data (Y/N) ?X
You must answer with a Y or N.

0 rows deleted.
This technique has the advantage of keeping your entire script in one file, but it's pretty much limited to handling the
case where you have several possible queries to execute and must choose the correct one based on input from the user.
Using REFCURSOR Variables to Simulate Branching
If you want to present the user with a choice of reports to run, you can place the conditional logic within PL/SQL and
use a REFCURSOR variable to return the selected query to SQL*Plus, where the results can be formatted and printed.
REFCURSOR variables are only available beginning with
SQL*Plus version 8 and above.
The following script gives the user a choice of three different reports. The conditional logic is implemented in a PL/

SQL block, and the results are returned to SQL*Plus via bind variables. A REFCURSOR bind variable is used to return
a query that generates the report requested by the user.
DESCRIPTION
Print one of three user security reports

SET FEEDBACK OFF
SET PAGESIZE 20
SET LINESIZE 77
SET HEADING ON

Ask the user what report to print
PROMPT
PROMPT 1 - List users
PROMPT 2 - List users and table privileges
PROMPT 3 - List users and system privileges

< previous page page_245 next page >
< previous page page_246 next page >
Page 246
PROMPT
ACCEPT s_report_choice PROMPT Enter your choice (1,2,3) >

A PL/SQL block will set the b_report bind variable
to a query based on the user's response. Text for the
report title will be returned in b_report_type.
VARIABLE b_report REFCURSOR
VARIABLE b_report_type VARCHAR2 (30)

Interpret the user's choice.
BEGIN

IF &&s_report_choice = 1 THEN
Return some text for the title to identify this report.
:b_report_type := User Listing;

Return a query that will list all users.
OPEN :b_report FOR
SELECT username
FROM dba_users
ORDER BY username;
ELSIF &&s_report_choice = 2 THEN
Return some text for the title to identify this report.
:b_report_type := User Table Privileges;

Return a query that will list users and any
privileges they have on tables in the database.
OPEN :b_report FOR
SELECT username, privilege, owner, table_name
FROM dba_users, dba_tab_privs
WHERE username = grantee
ORDER BY username, owner, table_name, privilege;
ELSIF &&s_report_choice = 3 THEN
Return some text for the title to identify this report.
:b_report_type := User System Privileges;

Return a query that lists users and any system
privileges they have been granted.
OPEN :b_report FOR
SELECT username, privilege
FROM dba_users, dba_sys_privs
WHERE username = grantee

ORDER BY username, privilege;
ELSE
Return some text for the title to identify this report.
:b_report_type := Invalid Report Choice;

The user made an invalid choice, so
return a query that will display an error message.
OPEN :b_report FOR
SELECT You must choose either 1, 2, or 3 error_message
FROM dual;
END IF;
END;
/

< previous page page_246 next page >
< previous page page_247 next page >
Page 247
Specify formats for all possible report columns.COLUMN
username FORMAT A12 HEADING User
COLUMN privilege FORMAT A20 HEADING Privilege
COLUMN owner FORMAT A12 HEADING Table Owner
COLUMN table_name FORMAT A30 HEADING Table Name
COLUMN error_message FORMAT A40 HEADING Error Message

Set up the page title. First we have to get the contents of
b_report_type into a substition variable.
set termout off

COLUMN b_report_type FORMAT A30 NOPRINT NEW_VALuE s_report_type
SELECT b_report_type FROM dual;

set termout on

TTITLE LEFT s_report_type RIGHT Page FORMAT 999 SQL.PNO SKIP 2

Run the report requested by the user
PRINT b_report
Note that the script contains COLUMN commands for all possible columns from the three different queries. These don't need to
be conditionally executed, because format definitions for columns not used in the final query are ignored by SQL8Plus. Also
note that the PL/SQL code does return a query even for the case where the user's input is invalid; this query simply selects an
error message from the DUAL table. Here is the output from running this script, first showing the results of an invalid input,
then showing the output from one of the reports:
SQL> @user_security

1 - List users
2 - List users and table privileges
3 - List users and system privileges

Enter your choice (1,2,3) >4

User Listing Page 1

Error Message

You must choose either 1, 2, or 3
SQL> @user_security

1 - List users
2 - List users and table privileges
3 - List users and system privileges


Enter your choice (1,2,3) >2

User Listing Page 1

< previous page page_247 next page >
< previous page page_248 next page >
Page 248
User Privilege Table Owner Table Name

SYSTEM EXECUTE SYS AQS_AGENT
SYSTEM EXECUTE SYS AQS_DEQUEUE_HISTORY
SYSTEM EXECUTE SYS AQS_HISTORY
SYSTEM EXECUTE SYS AQS_RECIPIENTS
SYSTEM EXECUTE SYS AQS_SUBSCRIBERS
SYSTEM EXECUTE SYS DBMS_ADAM
SYSTEM EXECUTE SYS DBMS_AQ_IMPORT_INTERVAL
SYSTEM EXECUTE SYS DBMS_DEFER_IMPORT_INTERVAL
SYSTEM ALTER SYS INCEXP
SYSTEM DELETE SYS INCEXP
SYSTEM INDEX SYS INCEXP
In this example, the query output is only displayed on the screen. If you wanted to print it, you would just need to add a SPOOL
command to send the output to a file, which you could later send to a printer.
Branching Using a Multilevel File Structure
The most generic and flexible approach to branching that you can implement using SQL*Plus is to write your script to execute
one of several alternative files based on user input or other criteria. This is best explained by example, so here is a simplified
version of the security reports menu shown previously in this chapter:
Ask the user what report to print
PROMPT
PROMPT 1 - List users
PROMPT 2 - List users and table privileges

PROMPT 3 - List users and system privileges
PROMPT
ACCEPT s_report_choice PROMPT Enter your choice (1,2,3) >

Execute the appropriate report
@user_security_&&s_report_choice
The key to this approach is in the last line, where the user's response is used to form the name of another SQL file to execute. If
the user chooses option 1, for example, the last line in the above script will be translated to:
@user_security_1
Of course, you have to make sure that a file named USER_SECURITY_1 .SQL exists and that it will generate the correct report.
When you use this approach to branching, you will end up with a set of script files that form an inverted tree structure. The tree
diagram in Figure 7-1 shows the relationship between the menu script and the scripts that run the individual reports.
Because this branching technique executes another SQL*Plus script, you can continue to ask the user questions, and even branch
again depending on the user's response. The one thing you have to watch out for is that SQL*Plus cannot nest

< previous page page_248 next page >
< previous page page_249 next page >
Page 249
Figure 7-1
Structure for the security reports menu, using a multilevel file structure
scripts indefinitely. SQL*Plus can currently nest scripts only 20 levels deep, and some older versions only allow 5
levels of nesting.
A useful variation on this technique is to code it using a SELECT statement to analyze the user's input and derive the
name of the next script to call. You get two benefits from this: the script names are not directly linked to the user's
input, and it's easier to designate one script to be called when the user makes an invalid choice. The only penalty is a
small amount of added complexity in your script. The following script is an example of this technique:
Ask the user what report to print
PROMPT
PROMPT A - List users
PROMPT B - List users and table privileges

PROMPT C - List users and system privileges
PROMPT
ACCEPT s_report_choice PROMPT Enter your choice (A,B,C) >

DECODE the user's input.
SET TERMOUT OFF
COLUMN user_choice NOPRINT NEwVALUE s_next_script
SELECT DECODE (UPPER ( &s_report_choice) ,
A,USER_SECURITY_1.SQL,
B,USER_SECURITY_2.SQL,
C,USER_SECURITY_3>SQL,
USER_SECURITY_ $.SQL)user_choice
FROM DUAL;
SET TERMOUT ON

Execute the appropriate report
@@&s_next_script
The key to this script is the call to DECODE in the SELECT statement. DECODE is a SQL function that allows you to
arbitrarily specify an output value for any given input value. In this case, the input value is UPPER(&s_report_choice).
By using

< previous page page_249 next page >
< previous page page_250 next page >
Page 250
the UPPER function, we allow the user to respond in either uppercase or lowercase. Following the input are three value
pairs, each specifying the output for a specific input value. An input of A causes this DECODE to return
USER_SECURITY_1.SQL, an input of B causes USER_SECURITY_2.SQL to be returned, and so forth. The final
value, USER_SECURITY_4.SQL, is returned if the user's choice did not match any of the others. In this case, the file
USER_SECURITY_4.SQL would display some sort of error message, telling the user what he did wrong.
If you decide to develop a set of scripts like this, it's best to spend some time up front working out the structure before

you begin scripting. Making changes after you've started writing a set of scripts like this can become cumbersome very
quickly because so many files are involved. Try to keep things as modular as possible, too. In this example, any of the
reports can be run as standalone scripts without going through the menu.
Using SQL to Write SQL
Another way to branch that also involves a multilevel file structure is simply to spool some output to a new SQL file,
then execute that file. To implement the security report menu using this technique, you could spool one of three
SELECT statements to a file based on the user's report choice. Here's a version of the script that does that:
DESCRIPTION
Print one of three user security reports

SET FEEDBACK OFF
SET PAGESIZE 20
SET LINESIZE 77
SET HEADING ON

Ask the user what report to print
PROMPT
PROMPT 1 - List users
PROMPT 2 - List users and table privileges
PROMPT 3 - List users and system privileges
PROMPT
ACCEPT s_report_choice PROMPT Enter your choice (1,2,3) >

Specify formats for all possible report columns.
COLUMN username FORMAT A12 HEADING User
COLUMN privilege FORMAT A20 HEADING Privilege
COLUMN owner FORMAT A12 HEADING Table Owner
COLUMN table_name FORMAT A30 HEADING Table Name
COLUMN error_message FORMAT A40 HEADING Error Message


Set up the page title. First we have to get the contents of
b_report_type into a substition variable.

< previous page page_250 next page >
< previous page page_251 next page >
Page 251
set termout off
COLUMN b_report_type FORMAT A30 NOPRINT NEW_VALUE s_report_type
SELECT DECODE (&&s_report_choice,
1,User List,
2,User Table Privileges,
3,User System Privileges,
Invalid Choice) b_report_type
FROM dual;
set termout on

TTITLE LEFT s_report_type RIGHT Page FORMAT 999 SQL.PNO SKIP 2

Generate the query for the report requested by the user.
Spool that query to a file.
SET TERMOUT OFF
SET PAGESIZE 0
SET HEADING OFF
SET VERIFY OFF
SET FEEDBACK OFF
COLUMN next_query FORMAT A60
SPOOL user_security_choice.sql

This query will be successful if the user chooses 1
SELECT SELECT username ¦¦ CHR(10) ¦¦

FROM dba_users ¦¦ CHR(10) ¦¦
ORDER BY username; ¦¦ CHR(10) next_query
FROM dual
WHERE &&s_report_choice = 1;

This query will be successful if the user chooses 2
SELECT SELECT username, privilege, owner, table_name ¦¦ CHR(10) ¦¦
FROM dba_users, dba_tab_privs ¦¦ CHR(10) ¦¦
WHERE username = grantee ¦¦ CHR(10) ¦¦
ORDER BY username, owner, table_name, privilege;
FROM dual
WHERE &&s_report_choice = 2;

SELECT SELECT username, privilege ¦¦ CHR(10) ¦¦
FROM dba_users, dba_sys_privs ¦¦ CHR(10) ¦¦
WHERE username = grantee ¦¦ CHR(10) ¦¦
ORDER BY username, privilege;
FROM dual
WHERE &&s_report_choice = 3;

SELECT PROMPT You must choose either 1, 2, or 3
FROM dual
WHERE &&s_report_choice NOT IN (1,2,3)
OR &&s_report_choice IS NULL;

SPOOL OFF
SET TERMOUT ON
SET PAGESIZE 20
SET HEADING ON
SET VERIFY ON


×