Here's an example: SQL> PRINT my_string MY_STRING --- Brighten the corner where you are The bind variable is treated like a database column, with the variable name being the default colu
Trang 1Is the name of the bind variable you want to print If you omit a name, the values of all bind variables are printed The results from the PRINT command look very much like the results you get from a SELECT Here's an example:
SQL> PRINT my_string
MY_STRING
-
Brighten the corner where you are
The bind variable is treated like a database column, with the variable name being the default column heading If you have page titles defined, they will print as well You can even use the COLUMN commands to format the output The following example shows how this works:
SQL> COLUMN my_string FORMAT A40 HEADING My Motto
SQL> PRINT my_string
My Motto
-
Brighten the corner where you are
SQL>
SQL> TTITLE LEFT This is a Page Title SKIP 2
SQL> COLUMN my_number FORMAT 99.99 HEADING My Age
SQL> PRINT my_number
This is a Page Title
My Age
-
36.00
All other formatting options, such as PAGESIZE and LINESIZE, apply when printing bind variables You can even use the COLUMN command's NEW_VALUE clause to store the value of a bind variable in a substitution variable, as the following example demonstrates:
SQL> DEFINE s_my_string =
SQL> COLUMN my_string NOPRINT NEW_VALUE s_my_string
SQL> PRINT my_string
SQL> PROMPT &&s_my_string
Brighten the corner where you are
Issuing the PRINT command by itself causes the contents of all bind variables to be displayed Here's an example:
SQL> PRINT
My Motto
-
Trang 2
Brighten the corner where you are
My Age
-
36.00
Some special considerations apply when printing bind variables of type CLOB and of type REFCURSOR These are described in the following sections.
Printing CLOB variables
The CLOB datatype is new with Oracle8 CLOB stands for character large object, and variables of this type can hold up to 2 gigabytes of text data When printing variables of type CLOB or NCLOB, there are three SQL*Plus settings you can use to control what you see and how that works See Table 7-2.
Table 7-2 Settings That Affect the Printing of CLOBs
setting Default Description
SET LONG 80 Controls the number of characters that are actually displayed from CLOB variable By
default, only the first 80 characters will print The rest are ignored.
SET LONGCHUNKSIZE 80 CLOB variables are retrieved from the database a piece at a time This setting controls
the size of that piece.
SET LOBOFFSET 1 An offset you can use to start printing with the nth character in the CLOB variable Be
default, SQL*Plus will begin printing with the first character A LOBOFFSET of 80, for example, skips the first 79 characters of the string.
By default, SQL*Plus will only display the first 80 characters of a CLOB value This is rarely enough After all, if you only needed 80 characters, you wouldn't have used a CLOB datatype in the first place On the other hand, you may not want to risk printing 2 gigabytes of data either.
The following example shows the result of printing a CLOB value using the default settings for the values in Table 7-2:
SQL> SELECT clob_value FROM clob_example;
CLOB_VALUE
-
By default, SQL*Plus will only display the first 80 characters of a CLOB value
1 row selected.
Trang 3
As you can see, only 80 characters of the value were displayed You can change the LONG setting to see more of the value, as the next example shows:
SQL> SET LONG 500
SQL> SELECT clob_value from clob_example;
CLOB_VALUE
-
By default, SQL*Plus will only display the first 80 characters of a CLOB
value This is rarely enough After all, if you only needed 80 characters,
you wouldn't have used a CLOB datatype in the first place
On the other hand, you may not want to risk printing 2 gigabytes of data either
1 row selected.
By combining the LOBOFFSET and LONG settings, you can print any arbitrary substring of a CLOB variable The following
example prints the characters 81 through 102, which make up the second sentence in the example:
SQL> SET LONG 22
SQL> SET LOBOFFSET 81
SQL> SELECT clob_value FROM clob_example;
CLOB_VALUE
-
This is rarely enough
1 row selected.
Finally, the LONGCHUNKSIZE setting controls the amount of the CLOB that is fetched from the database at one time If you have the memory available, you may want to set this to match the LONG setting That way SQL*Plus will retrieve the value with one fetch from the database, possibly improving performance.
Printing REFCURSOR variables
Beginning with Oracle8, SQLlus allows you to create bind variables of the type REFCURSOR A REFCURSOR variable is a pointer
to a cursor that returns a result set Using PL/SQL, you can assign any SELECT query to a variable of this type, and then you can use the SQL*Plus PRINT command to format and display the results of that query The following script makes use of this capability It asks the user for a pattern match string and prints the list tables with names that match the pattern supplied by the user A PL/SQL block is used to interpret the user's response, and to open a cursor that will return the desired results.
Find out what tables the user wants to see
A null response results in seeing all the tables
ACCEPT s_table_like PROMPT List tables LIKE >
VARIABLE l_table_list REFCURSOR
Trang 4
This PL/SQL block sets the l_table_list variable
to the correct query, depending on whether or
not the user specified all or part of a table_name
BEGIN
IF &&s_table_like IS NULL THEN
OPEN :l_table_list FOR
SELECT table_name
FROM user_tables;
ELSE
OPEN :l_table_list FOR
SELECT table_name
FROM user_tables
WHERE table_name LIKE UPPER (&&s_table_like);
END IF;
END;
/
Print the list of tables the user wants to see
PRINT l_table_list
As you can see, this script defines a SQL*Plus REFCURSOR variable The cursor is opened and a query assigned by code within a PL/SQL block Then the SQL*Plus PRINT command is used to display the results of that query
SQL> @ref_cursor
List tables LIKE >
TABLE_NAME
-
EMPLOYEE
PROJECT
PROJECT_HOURS
SQL>
SQL>
SQL> @ref_cursor
List tables LIKE > p%
TABLE_NAME
-
PROJECT
PROJECT_HOURS
The output you get when PRINTing a REFCURSOR variable is identical to the output you would get if you executed the same query directly from SQL*Plus
SELECTing a bind variable
The SQL*Plus manual, at least the one for versions 8.0.3 and before, will tell you that bind variables cannot be used in SQL statements Don't believe it Bind variables can be used in SELECT statements, both in the column list and in the WHERE clause You will frequently see this done in scripts where there is a need to get the contents of a bind variable into a substitution variable See the section
Trang 5
titled From bind to substitution earlier in this chapter Here's an example of a SELECT statement being used to display the contents of a bind variable:
SQL> VARIABLE l_user VARCHAR2(30)
SQL> EXECUTE ;l_user := user;
SQL> SELECT :l_user FROM dual;
:L_USER
-
NATHAN
Using SELECT like this offers no real advantage over the use of the PRINT command If you just need to display one variable, you might as well PRINT it Being able to use bind variables in a SELECT statement becomes more of an advantage when you need to display information from more than one column, when you want to use the bind variable in
an expression for a computed column, or when you want to use it in the WHERE clause Here's an example that
combines all three of these:
SQL> SET HEADING OFF
SQL> SELECT User ¦¦ :l_user ¦ ¦ has
2 ¦ ¦ TO_CHAR(COUNT(*)) ¦ ¦ tables
3 FROM all_tables
4 WHERE owner = :l_user;
NATHAN
User SQLPLUS has 3 tables
Two types of bind variables cannot be used in a SQL statement These are the REFCURSOR and CLOB types You must use the PRINT command with these
When and How to Use Bind Variables
There are three primary reasons to use bind variables in SQL*Plus:
You need to call PL/SQL procedures or functions that return a value or that use IN OUT parameters
You need to conditionally execute one of several possible SELECT statements depending on user input or other
circumstances
You want to test a query for use in an application, and that query uses colons to mark parameters
The next few sections briefly describe each of these uses
Calling PL/SQL procedures and functions from SQL*Plus
Oracle provides a number of built-in PL/SQL packages that allow you to do such things as create and execute dynamic SQL queries (the DBMS_QL package), submit and manage PL/SQL batch jobs (the DBMS_JOBS package), and many other very useful things In many cases, the procedures and functions in these packages
Trang 6
use OUT parameters to return information to the caller To call these routines from within a SQL*Plus script and
display the results to the person running the script, you need to use bind variables
Consider the problem of writing a SQL*Plus script to submit a PL/SQL job for periodic execution by Oracle To do this, you would use a procedure in Oracle's DBMS_JOBS package named SUBMIT SUBMIT takes several arguments, one of which is an OUT argument The declaration for SUBMIT looks like this:
DBMS_JOB SUBMIT( job OUT BINARY_INTEGER,
what IN VARCHAR2,
next_date IN DATE DEFAULT SYSDATE,
interval IN VARCHAR2 DEFAULT null,
no_parse IN BOOLEAN DEFAULT FALSE)
The job parameter is the job number, and is an output from the procedure This job number uniquely identifies a job within Oracle, and is important to know because all the procedures to modify, delete, or otherwise manage database jobs require the job number as an input Here is one approach you could use to write a script that submits a PL/SQL job for a user In particular, note the use of a bind variable in the EXECUTE command Also note the subsequent PRINT
command that displays the job number to the user
SET ECHO OFF
SET FEEDBACK OFF
SET VERIFY ON
SET HEADING ON
SET TERMOUT ON
This script submits a job to the queue after first
prompting the user for the required information
Get the neccessary information from the user
PROMPT
prompt Submit a PL/SQL job
PROMPT
ACCEPT what CHAR PROMPT Enter the PL/SQL statement to execute >
PROMPT
ACCEPT when CHAR PROMPT When (e.g 15-Nov-1961 18:30)?
ACCEPT interval CHAR PROMPT Interval (e.g sysdate+1)?
Submit the job
VARIABLE job_number NUMBER
EXECUTE DBMS_JOB.SUBMIT(:job_number, &what, &when, &interval);
Tell the user the job's ID number
PRINT job_number
SET FEEDBACK ON
SET VERIFY ON
Trang 7
Running the above script looks like this:
SQL> @submit_job
Submit a PL/SQL job
Enter the PL/SQL statement to execute >NULL;
When (e.g 15-Nov-1961 18:30)?sysdate
Interval (e.g sysdate+1)?sysdate+1
JOB_NUMBER
-
4
NULL is a valid PL/SQL statement that does nothing, and is often used as a place-holder for code to be written later In this example, Oracle will do nothing once each day
Using REFCURSOR variables
As mentioned earlier, the REFCURSOR datatype holds a pointer to a cursor, and is a new feature introduced with
Oracle8 Using REFCURSOR variables, you can open a cursor for a SELECT statement in PL/SQL and print the results from SQL*Plus One practical use for this is to write PL/SQL code that selects one query from many possibilities, based
on user input or some other factor
Earlier, in the section on printing, you saw a REFCURSOR example that printed a list of tables owned by the current user Here is an enhanced version of that script that allows the user to optionally enter a pattern match string to narrow the list of table names to be displayed The script executes one of two possible queries depending on whether or not a string was supplied
Find out what tables the user wants to see
A null response results in seeing all the tables
ACCEPT s_table_like PROMPT List tables LIKE >
VARIABLE l_table_list REFCORSOR
This PL/SQL block sets the l_table_list variable
to the correct query, depending on whether or
not the user specified all or part of a table_name
BEGIN
IF &&s_table_like IS NULL THEN
OPEN : l_table_list FOR
SELECT table_name
FROM user_tables;
ELSE
OPEN :l_table_list FOR
SELECT table_name
FROM user_tables
WHERE table_name LIKE UPPER(&&s_table_like);
Trang 8
END IF;
END;
/
Print the list of tables the user wants to see
PRINT l_table_list
This script first asks the user for a search string to be used with the LIKE operator Entering this is optional If a pattern match string is specified, then only table names that match that string are displayed; otherwise, all table names are listed This conditional logic is implemented by the PL/SQL block, which checks the value of the substitution variable and then opens the REFCURSOR variable using the appropriate SELECT statement Here's how it looks to run the script:
SQL> @ref_cursor
List tables LIKE >
TABLE_NAME
-
EMPLOYEE
PROJECT
PROJECT_HOURS
SQL>
SQL>
SQL> @ref_cursor
List tables LIKE > p%
TABLE_NAME
-
PROJECT
PROJECT_HOURS
You can see that when no search string was specified, all the tables were listed, whereas entering a search string of p% caused only tables starting with the letter P to be listed The output is just like that from a standard SELECT statement, and all the column and page formatting options of SQL*Plus apply
You might be thinking now about using REFCURSOR variables with the DBMS_SQL package to return the results of dynamically generated SQL queries back to
SQL*Plus Unfortunately, that can't be done DBMS_SQL returns integer values that
reference cursors held internally, but there is no way to get a REFCURSOR value pointing to
one of those cursors
Using REFCURSOR variables is one way to add conditional logic to your SQL*Plus scripts You'll see another
example of this later in the section titled Branching in SQL*Plus
Trang 9
Testing application queries
Bind variables can make it more convenient to take a query from an application development environment such as PowerBuilder and debug it using SQL*Plus PowerBuilder queries often contain parameters to be supplied at runtime PowerBuilder parameters are preceded by colons, the same syntax SQL*Plus uses for bind variables If you had a
PowerBuilder datawindow that allowed you to edit just one employee record, here is how the query behind that
datawindow might look:
SELECT employee.employee_id,
employee.employee_name,
employee.employee_hire_date,
employee.employee_termination_date,
employee.employee_billing_rate
FROM employee
WHERE employee.employee_id = :emp_id
Now if you want to test this query, and you just paste it into SQL*Plus as it is, you will get the following results:
SQL> SELECT employee employee_id,
2 employee.employee_name,
3 employee.employee_hire_date,
4 employee employee_termination_date,
5 employee.employee_billing_rate
6 FROM employee
7 WHERE employee employee_id = :emp_id
8 /
Bind variable EMP_ID not declared
At this point, you have two choices You can change the query and simply replace the parameter :emp_id with an
employee number that you know exists Then you can test the query, and when you are satisfied the query works, you can replace the hardcoded value with the parameter reference Woe be unto you, however, if there are several
parameters and you forget to change one back A safer approach would be simply to declare bind variables to match the parameters in the query In this case there's just one to declare:
SQL> VARIABLE emp_id NUMBER
Once the variable has been declared, it is a simple matter to initialize it to a known good value:
SQL> EXECUTE :emp_id := 101;
PL/SQL procedure successfully completed
Having declared and initialized the variable, it's now a simple matter to copy the query directly from PowerBuilder, or wherever, and paste it unchanged into SQL*Plus:
Trang 10
SQL> SELECT employee.employee_id,
2 employee.employee_name,
3 employee.employee_hire_date,
4 employee.employee_termination_date,
5 employee.employee_billing_rate
6 FROM employee
7 WHERE employee.employee_id = :emp_id
8 /
EMPLOYEE_ID EMPLOYEE_NAME EMPLOYEE_ EMPLOYEE_ EMPLOYEE_BILLING_RATE
- - - - -
101 Jonathan Gennick 15-NOV-61 169
Once you are satisfied that everything is correct, you can paste the query directly back into your application without the risk that you might forget to manually change a hardcoded value back into a parameter
Branching in SQL*Plus
SQL*Plus has no IF statement This is a very vexing thing Script writing is similar to programming It's natural to want to take different actions depending on user input or some other condition Imagine how frustrated you would be if your
favorite programming language suddenly lost its IF statement! Despite the lack of an IF statement in SQL*Plus, there are some approaches you can take to get equivalent results Some are more straightforward than others All involve some
compromises
Approaches to Branching
There are at least six approaches you can take to the problem of conditional execution These are:
Simulate branching by adjusting the WHERE clause in a query
Use REFCUSOR variables
Use a multilevel file structure
Use SQL to write SQL
Use PL/SQL for conditional logic
Use an operating-system scripting language
Some of these approaches are very specific to certain types of problems Using REFCURSOR variables, for example, is a good solution when you simply need to choose which query to run based on user input or some other condition Other approaches, such as the use of a multilevel file structure for your script, are more general in nature, and can be used for any type of branching