NOTE: The common understanding among long−time Oracle programmers is that when you parse DDL, it always executes, so a call to the EXECUTE procedure is not necessary when calling DBMS_SQ
Trang 1instructed by your DBA.
Note that you cannot defer the parsing of this statement, as is possible with OCI Statements in DBMS_SQL are parsed immediately Oracle documentation does mention that this "may change in future versions; you should not rely on this behavior." This means that at some point in the future, Oracle Corporation may allow parsing to be deferred to the execute phase, thereby reducing network traffic If this change occurs, let's hope that a flag is offered to preserve earlier functionality
NOTE: The common understanding among long−time Oracle programmers is that when you
parse DDL, it always executes, so a call to the EXECUTE procedure is not necessary when
calling DBMS_SQL.PARSE for a DDL statement You should not take this shortcut! Oracle
will not guarantee that this behavior will continue in future releases If you want to make sure
that your DDL has executed, call the DBMS_SQL.EXECUTE procedure
2.3.3.2 Parsing very long SQL statements
PL/SQL8 offers a second, overloaded version of PARSE, which comes in handy when you have very large SQL statements If your SQL statement exceeds the largest possible contiguous allocation on your system (and it is machine−dependent) or 32Kbytes (the maximum size for VARCHAR2), then use this version of the PARSE procedure:
PROCEDURE DBMS_SQL.PARSE
(c IN INTEGER,
statement IN DBMS_SQL.VARCHAR2S,
lb IN INTEGER,
ub IN INTEGER,
lfflg IN BOOLEAN,
language_flag IN INTEGER);
The parameters for this procedure are summarized in the following table
Parameter Description
c The pointer to the cursor or memory area for this SQL statement
statement The SQL statement to be parsed and associated with the cursor In this case, you will be
passing a PL/SQL table of the DBMS_SQL.VARCHAR2S type
lb The lower bound or first row in the statement table to be parsed
ub The upper bound or last row in the statement table to be parsed
lfflg If TRUE, then a line−feed should be concatenated after each row in the table
language_flag A flag determining how Oracle will handle the statement Valid options are DBMS_SQL.V6,
DBMS_SQL.V7, and DBMS_SQL.NATIVE Use DBMS_SQL.NATIVE unless otherwise instructed by your DBA
My own parse_long_one procedure offers an example of using the array−based version of the PARSE
procedure:
/* Filename on companion disk: parslong.sp */
CREATE OR REPLACE PROCEDURE parse_long_one
(select_list IN VARCHAR2,
from_list IN VARCHAR2,
where_clause IN VARCHAR2,
maxlen IN BINARY_INTEGER := 256, /* Can change the max */
dbg IN BOOLEAN := FALSE /* Built−in debugging toggle */
)
IS
/* Open the cursor as I declare the variable */
cur BINARY_INTEGER := DBMS_SQL.OPEN_CURSOR;
Trang 2/* Declare the index−by table based on the DBMS_SQL TYPE */
sql_table DBMS_SQL.VARCHAR2S;
/* Local module to extract up to the next maxlen chars */
FUNCTION next_row
(string_in IN VARCHAR2,
start_inout IN OUT BINARY_INTEGER,
len_in IN BINARY_INTEGER)
RETURN VARCHAR2
IS
v_start BINARY_INTEGER := start_inout;
BEGIN
start_inout := LEAST (len_in + 1, start_inout + maxlen);
RETURN SUBSTR (string_in, v_start, maxlen);
END;
/* Local module to transfer string to index−by table */
PROCEDURE fill_sql_table (string_in IN VARCHAR2)
IS
v_length BINARY_INTEGER;
v_start BINARY_INTEGER := 1;
BEGIN
IF string_in IS NOT NULL
THEN
v_length := LENGTH (string_in);
LOOP
sql_table (NVL (sql_table.LAST, 0)+1) :=
next_row (string_in, v_start, v_length);
EXIT WHEN v_start > v_length;
END LOOP;
END IF;
END;
BEGIN
/* Move each portion of the SELECT string to the table */
fill_sql_table (select_list);
fill_sql_table (from_list);
fill_sql_table (where_clause);
/* Parse everything from first to last row of table */
DBMS_SQL.PARSE (cur,
sql_table, sql_table.FIRST, sql_table.LAST,
FALSE, DBMS_SQL.NATIVE);
/* Execute and fetch rows if doing something for real */
/* If debugging, then display contents of the table */
IF dbg
THEN
DBMS_OUTPUT.PUT_LINE
('Parsed into lines of length ' || TO_CHAR (maxlen));
FOR rowind IN sql_table.FIRST sql_table.LAST
LOOP
DBMS_OUTPUT.PUT_LINE (sql_table(rowind));
END LOOP;
END IF;
/* Close the cursor when done */
DBMS_SQL.CLOSE_CURSOR (cur);
END;
/
Here is a little test script (and the results of execution) for this procedure:
SQL> BEGIN
parse_long_one ('select empno, ename, sal, hiredate, mgr, comm ',
'from emp ', 'where empno = empno and sal = sal', 10, TRUE);
Trang 3/
Parsed into lines of length 10
select emp
no, ename,
sal, hire
date, mgr,
comm
from emp
where empn
o = empno
and sal =
sal
/
Notice that the SELECT statement is broken without any concern for keeping identifiers intact The lfflg value passed in to the PARSE procedure is set to FALSE so linefeeds are not concatenated As a result, the broken identifiers are concatenated back together and the SQL statement parses without any difficulty
2.3.4 Binding Values into Dynamic SQL
The SQL (or PL/SQL) statement you execute is constructed as a string at runtime In most scenarios, you are using dynamic SQL because all the information about the SQL statement is not known at compile time You
therefore have values that you want to pass into the SQL statement at runtime You have two ways of doing
this: concatenation and binding With concatenation, you convert all elements of the SQL statement into
strings and concatenate them together With binding, you insert placeholders in your string (identifiers
prefaced with a colon) and then explicitly bind or associate a value with that placeholder before executing the SQL statement
If you concatenate the value into the string, then you are not really binding values and you do not have to make calls to the BIND_VARIABLE or BIND_ARRAY procedures Here is an example of the parsing of a dynamically constructed string relying on concatenation:
DBMS_SQL.PARSE
(cur, 'SELECT * FROM emp WHERE ename LIKE ' || v_ename);
At runtime the string is cobbled together and passed to the SQL engine for parsing With binding, you would write code like this:
DBMS_SQL.PARSE
(cur, 'SELECT * FROM emp WHERE ename LIKE :varname');
DBMS_SQL.BIND_VARIABLE (cur, 'varname', var_name_in);
Binding involves writing more code, but offers much more flexibility and power The following comparison between concatenation and binding techniques will help you decide which to use:
•
When you concatenate, you convert to a string format This can become awkward and error−prone With binding, you do not perform any conversions Instead, the native datatypes are employed
•
When you execute DDL statements dynamically, you cannot use bind variables Your only choice is
to concatenate together the strings and then pass that to the engine This makes sense, since, at least in the current version of DBMS_SQL, there is no such thing as deferred parsing When you parse, you also execute DDL
•
Trang 4You can execute the same dynamic cursor more than once, and each time you bind in different values
to the SQL statement This is not possible if you concatenate the values into the string at the time of parsing
•
With bind variables, you can take advantage of the new array−processing features of PL/SQL8's DBMS_SQL package You can bind an entire array of scalar values into the SQL string and then apply each of those values in a single SQL execution
So if you decide that you really do want to bind variables into your dynamic SQL, use one of the programs described in the following sections
2.3.4.1 The DBMS_SQL.BIND_VARIABLE procedure
The BIND_VARIABLE procedure binds a scalar value to a placeholder in your SQL statement A placeholder
is an identifier prefaced by a colon, as in :myval Call BIND_VARIABLE after DBMS_SQL.PARSE, but before calls to EXECUTE and EXECUTE_AND_FETCH This procedure is overloaded to allow you to bind
a number of different types of data This is the header:
PROCEDURE DBMS_SQL.BIND_VARIABLE
(c IN INTEGER,
name IN VARCHAR2,
value IN <datatype>);
The parameters for this procedure are summarized in the following table
Parameter Description
c The handle or pointer to the cursor originally returned by a call to OPEN_CURSOR
name The name of the placeholder included in the SQL statement passed to PARSE
value The value to be bound to the placeholder variable
<datatype> may be any of the following:
BFILE
BLOB
CLOB CHARACTER SET ANY_CS
DATE
MLSLABEL /*Trusted Oracle only*/
NUMBER
VARCHAR2 CHARACTER SET ANY_CS_ARRAY
Here is an example of binding the current date/time into a placeholder called "now:"
DBMS_SQL.BIND_VARIABLE (cur, 'now', SYSDATE);
Here is an example of binding the literal value "Liberation Theology" into a placeholder called "progress:"
DBMS_SQL.BIND_VARIABLE (cur, ':progress', 'Liberation Theology');
Notice that you can include or leave out the colon when you specify the placeholder name
The DBMS_SQL package also offers more specific variants of BIND_VARIABLE for less−common
datatypes,
PROCEDURE DBMS_SQL.BIND_VARIABLE
(c IN INTEGER,
name IN VARCHAR2,
Trang 5value IN VARCHAR2 CHARACTER SET ANY_CS,
[,out_value_size IN INTEGER]);
PROCEDURE DBMS_SQL.BIND_VARIABLE_CHAR
(c IN INTEGER,
name IN VARCHAR2,
value IN CHAR CHARACTER SET ANY_CS,
[,out_value_size IN INTEGER]);
PROCEDURE DBMS_SQL.BIND_VARIABLE_RAW
(c IN INTEGER,
name IN VARCHAR2,
value IN RAW
[,out_value_size IN INTEGER]);
PROCEDURE DBMS_SQL.BIND_VARIABLE_ROWID
(c IN INTEGER,
name IN VARCHAR2,
value IN ROWID);
where out_value_size is the maximum size expected for the value that might be passed to this variable Square brackets indicate optional parameters If you do not provide a value for out_value_size, the size is the length
of the current value provided
2.3.4.1.1 Examples
For every placeholder you put in your SQL string, you must make a call to BIND_VARIABLE (or
BIND_ARRAY) For example, the SELECT statement in the call to PARSE below contains two bind
variables, :call_date and :call_type:
DBMS_SQL.PARSE
(the_cursor,
'SELECT COUNT(*) freq FROM call WHERE call_date = :call_date ' ||
'AND call_type_cd = :call_type',
DBMS_SQL.V7);
I will therefore need to issue the following two calls to BIND_VARIABLE before I can execute the query,
DBMS_SQL.BIND_VARIABLE (the_cursor, 'call_date', :call.last_date_called);
DBMS_SQL.BIND_VARIABLE (the_cursor, 'call_type', :call.call_status);
where the two bind values are items in an Oracle Forms screen Since BIND_VARIABLE is overloaded, I can call it with either a date value or a string, and PL/SQL will execute the appropriate code Notice that the name
of the bind variable does not have to match any particular column name in the SELECT statement, and it does
not have to match the name of the PL/SQL variable that may hold the value The name is really just a
placeholder into which a value is substituted
You can also include the colon in the placeholder name when you bind the value to the variable:
DBMS_SQL.BIND_VARIABLE (the_cursor, ':call_date', :call.last_date_called);
If you want to avoid having to make these separate calls to BIND_VARIABLE, you can substitute these values into the SQL statement yourself at the time the statement is parsed The code shows the same SELECT statement, but without any bind variables
DBMS_SQL.PARSE
(the_cursor,
'SELECT COUNT(*) freq FROM call WHERE call_date = ''' ||
TO_CHAR (:call.last_date_called) ||
''' AND call_type_cd = ''' || :call.call_status || '''',
DBMS_SQL.V7);