1. Trang chủ
  2. » Công Nghệ Thông Tin

Oracle SQL Plus The Definitive Guide- P29 doc

10 218 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 98,47 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The PL/SQL version of the above script looks like this: SET VERIFY OFF ACCEPT s_delete_confirm PROMPT Delete project hours data Y/N?. In this case, the command: @&&s_next_script will be

Trang 2

SET FEEDBACK ON

CLEAR COLUMNS

TTITLE OFF

You have to be very careful when using this technique to turn off anything that could cause extraneous text to be written

to the temporary command file This includes page headings, column headings, and verification That's why the script in the example included these commands:

SET TERMOUT OFF

SET PAGESIZE 0

SET HEADING OFF

SET VERIFY OFF

Terminal output was turned off to prevent the user from seeing the results of the SELECT on the display One last thing you have to worry about is the filename itself In the example shown above, the filename is hardwired into the script and does not include a path Because no path is specified, the file will be written to the current directory That's why a single ampersand or @ was used to run the intermediate file Using @ causes SQL*Plus to look in the current directory for the script

Having the filename hardwired into the script can cause problems if multiple users execute the script at the same time and from the same directory If you are concerned about this, you could write some SQL or PL/SQL code to generate a unique filename based on the Oracle username or perhaps the session identifier (SID) from the V $ SESSION view

Be creative with this technique You don't need to limit yourself to writing SQL*Plus scripts, either You can use

SQL*Plus to generate shell script files, SQL*PLoader files, DOS batch files, or any other type of text file

Using PL/SQL

Always consider the possibility of using PL/SQL to implement any type of complex procedural logic After all, that's the reason PL/SQL was invented in the first place If you can manage to prompt the user up front for any needed

information, and if you don't need to interact with the user during the operation, PL/SQL is the way to go

Trang 3

Page 253

The reports menu could not possibly be implemented in Pl/SQL because the menu needs to run another SQL*Plus script corresponding to the user's choice PL/SQL runs inside the database, and cannot invoke a SQL*Plus script

An ideal candidate for the use of PL/SQL would be the example where we asked the user a simple yes/no question, and then deleted data from the PROJECT_HOURS table if the user responded with a Y Here's how that script looked:

SET VERIFY OFF

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

DELETE

FROM project_hours

WHERE UPPER(&&s_delete_confirm) = Y;

This script works, and because the DELETE statement is so simple, it's not too hard to understand Still, there are

people who would look at it and become very confused The more complicated the WHERE clause gets, the greater the likelihood of confusion Wrapping a simple IF statement, which everyone understands, around the DELETE statement would add clarity to the script The PL/SQL version of the above script looks like this:

SET VERIFY OFF

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

SET SERVEROUTPUT ON

DECLARE

users_yn_response CHAR := UPPER(&&s_delete_confirm);

BEGIN

IF users_yn_response = Y THEN

DELETE

FROM project_hours;

COMMIT;

DBMS_OUTPUT.PUT_LINE(All PROJECT_HOURS data has been deleted.);

ELSIF users_yn_response = N THEN

DBMS_OUTPUT.PUT_LINE(No data was deleted.);

ELSE

DBMS_OUTPUT.PUT_LINE(You must answer with a Y or N.);

END IF;

EXCEPTION

WHEN OTHERS THEN

DBMS_OUTPUT.PUT_LINE(The PROJECT_HOURS data could not be deleted

¦¦ SQLERRM);

ROLLBACK;

END;

/

Trang 5

Page 254

This script is a bit longer, but it's also more robust The script will roll back the operation if the DELETE fails for any

reason It's also very clear now that the DELETE statement is going to delete all rows from the table.

Using a Scripting Language Instead

Don't overlook the possibility that you can use your operating-system scripting language to good advantage Any Unix shell will allow you to write more complex scripts than you could using SQL*Plus alone Here's an implementation of the user security report menu using the Unix Korn shell:

while:

do

print

print 1 - List users

print 2 - List users and table privileges

print 3 - List users and system privileges

print 4 - Quit

print

print -n Enter your choice (1,2,3,4) >

read

case $ REPLY in

1 )

sqlplus -s jgennick/beaner @user_security_1

;;

2 )

sqlplus -s jgennick/beaner @user_security_2

;;

3 )

sqlplus -s jgennick/beaner @user_security_3

;;

4 )

exit

;;

* )

print Please enter 1 , 2, 3, or 4

;;

esac

done

Peri is also something you should consider The Perl scripting language is available for both Unix and Windows It has the advantages of being widely used, and of not tying you to one specific operating system

Looping in SQL*Plus

There is no way to write a real loop using SQL*Plus Your best option, if you need to do something iteratively, is to use PL/SQL PL/SQL, however, doesn't allow you

Trang 6

Recursive execution

Generating a file of commands, and then executing it

The first option has some severe limitations, and I don't recommend it too strongly The second option I use all the time, especially when performing database maintenance tasks

Recursive Execution

You can't loop, but you can execute the same script recursively Say you have a script that displays some useful

information, and you want to give the user the option of running it again You can do that by recursively executing the script Take a look at the following interaction, in which the user is looking at indexes for various tables It looks like a loop Each time through, the user is prompted for another table name, and the indexes on that table are displayed

SQL> @list_indexex employee

INDEX_NAME COLUMN_NAME

- -

EMPLOYEE_PK EMPLOYEE_ID

Next table >project

INDEX_NAME COLUMN_NAME

- -

PROJECT_PK PROJECT_ID

PROJECT_BY_NAME PROJECT_NAME

Next table >project_hours

INDEX_NAME COLUMN_NAME

- -

PROJECT_HOURS_PK PROJECT_ID

EMPLOYEE_ID

TIME_LOG_DATE

Next table >

Thank you for using LIST_INDEXES Goodbye!

It sure does look like a loop, but it's not Here is the LIST_INDEXES script that is being run:

COLUMN index_name FORMAT A30

COLUMN column_name FORMAT A30

BREAK ON index_name NODUPLICATES

Trang 7

Page 256

SELECT index_name, column_name

FROM user_ind_columns

WHERE table_name = UPPER (1);

Ask the user if he wants to do this again

PROMPT

ACCEPT s_next_table PROMPT Next table >

Execute either list_indexes.sql or empty.sql,

depending on the user's response

COLUMN next_script NOPRINT NEW_VALUE s_next_script

SET TERMOUT OFF

SELECT DECODE (&&s_next_table,

,empty.sql,

list_indexes ¦¦ UPPER (&&s_next_table)) next_script

FROM dual;

SET TERMOUT ON

@&&s_next_script

The key to the looping is in the last part of the script, following the ACCEPT statement If the user enters another

tablename, the SELECT statement will return another call to the LIST_INDEXES script So when the user types project

in response to the prompt, the s_next_script substitution variable ends up being:

list_indexes PROJECT

The only thing missing is the ampersand sign, and that is supplied by the command at the bottom of the script In this case, the command:

@&&s_next_script

will be translated to:

@list_indexes PROJECT

If the user doesn't enter a table name at the prompt, the s_next_table variable will be null, and the DECODE statement

will return empty.sql EMPTY.SQL is necessary because the @ command must be executed EMPTY.SQL gives you a clean way out of the recursion In this case EMPTY.SQL prints a message, and is implemented like this:

PROMPT

PROMPT Thank you for using LIST_INDEXES Goodbye!

PROMPT

Recursive execution is a very limited technique You can't nest scripts forever SQL*Plus limits you to nesting only 20 scripts, and on some older versions the limit may be as low as 5 Exceed that limit, and you will get the following

message:

SQL*Plus command procedures may only be nested to a depth of 20

Trang 8

Generating a File of Commands

If you need to loop a fixed number of times, say once for each table you own, you can use a SQL query to build a

second file of SQL commands, then execute that file This is known as using SQL to write SQL, and it's a very powerful

scripting technique You've already seen an example of this technique in a previous section, where it was used to

implement the equivalent of an IF statement in a SQL*Plus script This technique can also be used to perform a

repetitive operation, if the basis for that operation can be the results of a SQL query

Suppose, for instance, that you want to write a script that will analyze and compute statistics for all your tables You don't want to hardcode the table names in the script, because then you would need to remember to change the script each time you drop or create a table Thinking in terms of pseudocode, you might envision a script like this:

FOR xxx = FIRST_TABLE TO LAST_TABLE

ANALYZE xxx COMPUTE STATISTICS;

NEXT XXX

Of course, that script would never fly in SQL*Plus Instead, you need a way to execute the ANALYZE command for each table without really looping One way to do that is to write a SQL query to create that command Take a look at the follow ing query:

SELECT ANALYZE ¦¦ table_name ¦¦ COMPUTE STATISTICS;

FROM user_tables;

Since SQL is a set-oriented language, it will return a result set consisting of one instance of the ANALYZE command for each table you own Running this query against the sample database used for this book will give the following

results:

ANALYZE EMPLOYEE COMPUTE STATISTICS;

ANALYZE PROJECT COMPUTE STATISTICS;

ANALYZE PROJECT_HOURS COMPUTE STATISTICS;

At this point, if this were just a once-off job, and if you were using a GUI version of SQL*Plus, you could simply copy the output and paste it back in as input The commands would execute and all your tables would be analyzed To make a script you can run periodically, all you need to do is to spool the ANALYZE commands to a file and then execute that file The following script does that

Trang 9

Page 258

SET ECHO OFF

DESCRIPTION

Analyze and compute statistics for all tables

owned by the user

We only want the results of the query written to the

file Headings, titles, feedback, etc aren't valid

commands, so turn all that stuff off

SET HEADING OFF

SET PAGESIZE 0

SET VERIFY OFF

SET FEEDBACK OFF

SET TERMOUT OFF

Create a file of ANALYZE commands, one for

each table PROMPT commands are used to write

SET ECHO ON and OFF commands to the spool file

SPOOL analyze_each_table.sql

PROMPT SET ECHO ON

SELECT ANALYZE TABLE ¦¦ table_name ¦¦ COMPUTE STATISTICS;

FROM user_tables;

PROMPT SET ECHO OFF

SPOOL OFF

Execute the ANALYZE commands

SET TERMOUT ON

@analyze_each_table

Reset settings back to defaults

SET HEADING ON

SET PAGESIZE 14

SET VERIFY ON

SET FEEDBACK ON

Most of the commands in the script are there to prevent any extraneous information, such as column headings or page titles, from being written to the spool file The real work is done by the SPOOL and SELECT commands, and also by the command used to run the resulting script file The ECHO setting is turned on prior to running the ANALYZE

commands so you can watch the ANALYZE commands as they execute A PROMPT command is used to write a SET ECHO ON command to the output file Here are the results from running the script:

SQL> @analyze_tables

SQL> ANALYZE TABLE EMPLOYEE COMPUTE STATISTICS;

SQL> ANALYZE TABLE PROJECT COMPUTE STATISTICS;

SQL> ANALYZE TABLE PROJECT_HOURS COMPUTE STATISTICS;

SQL> SET ECHO OFF

All the tables in the schema were analyzed Once this is set up, you never need to worry about it again Each time the script is run, ANALYZE commands are generated for all the tables currently in the schema

Trang 10

Looping Within PL/SQL

You should always consider PL/SQL when you need to implement any type of complex procedural logic, and that includes looping Because PL/SQL executes in the database, you can't use it for any type of loop that requires user interaction The table index example shown earlier in this chapter, where the user was continually prompted for another table name, could never be implemented in PL/SQL It's also impossible to call another SQL*Plus script from PL/SQL However, if you can get around those two limitations, PL/SQL may be the best choice for the task

The ANALYZE TABLE script revisited

As an example of what you can do using PL/SQL, let's revisit the ANALYZE_TABLE script shown earlier It's very easy, using PL/SQL, to write a loop to iterate through all the tables you own Here's one way to do that:

SQL> SET SERVEROUTPUT ON

SQL> BEGIN

2 FOR my_table in (

3 SELECT table_name

4 FROM user_tables) LOOP

5

5 Print the table name

6 DBMS_OUTPUT.PUT_LINE(my_table.table_name);

7 END LOOP;

8 END;

9 /

EMPLOYEE

PROJECT

PROJECT_HOURS

PL?SQL procedure successfully completed

This example uses what is called a cursor FOR loop A cursor FOR loop executes once for each row returned by the

query you give it In this example, that query returns a list of tables you own

You might think you could just put an ANALYZE TABLE command inside the loop and pass it the table name as a parameter, but it's not quite that simple The ANALYZE command is a DDL command, and prior to version 8.1 of Oracle, PL/SQL did not allow you to embed DDL commands within your code The following is an example of what will happen if you try

Ngày đăng: 05/07/2014, 04:20

TỪ KHÓA LIÊN QUAN