I think my problem is best described by an example:
Declare
example1 varchar2(300) := 'sysdate';
example2 varchar2(300) := 'null';
example3 varchar2(300) := 'user';
example4 varchar2(300) := '''Just some Text''';
cursor_name INTEGER;
rows_processed INTEGER;
BEGIN
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, 'UPDATE table_name SET column = :x', DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(cursor_name, ':x', example1);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
end;
/
All the "exampleX" variables will be bound as a varchar2 and not be "translated".
I previously used Execute Immediate but had to switch to DBMS_SQL because of performance optimizations. With Execute Immediate there are of course no problems if you use this approach:
Execute Immediate 'UPDATE table_name SET column = ' || example1;
But I can't think of a way to archieve this with BIND_VARIABLE.
(Of course I could concat the variables at the PARSE statement like with Execute Immediate but I think I will loose performance this way. Performance is highly important in this case)
Edit:
An example, closer to reality would be:
Copying data from one DB(SRC) to another DB(DEST), where I have this helper table:
CREATE TABLE "DEST_TAB_COLUMNS"
( "OWNER" VARCHAR2(30 BYTE),
"TABLE_NAME" VARCHAR2(30 BYTE),
"COLUMN_NAME" VARCHAR2(30 BYTE),
"DATA_TYPE" VARCHAR2(106 BYTE),
"OPERATION_TYPE" VARCHAR2(30 BYTE),
"OPERATION_FUNCTION" VARCHAR2(200 BYTE)
) ;
In this Table I define which columns I am interested in on the DEST side. And I have the option to define a "OPERATION_FUNCTION" to replace a certain column value.
So an entry would look like:
SRC_OWNER_NAME | SAMPLE_TABLE | SAMPLE_COL | VARCHAR2 | REPLACE | 'null'
SRC_OWNER_NAME | SAMPLE_TABLE | SAMPLE_COL2 | DATE | REPLACE | sysdate
On the SRC side I define which data I want to have transvered. This is a simple table which looks basically like:
CREATE TABLE "SRC_TRANSFER_DATA"
( "OWNER" VARCHAR2(30 BYTE),
"TABLE_NAME" VARCHAR2(30 BYTE),
"WHERE_CLAUSE" VARCHAR2(300 BYTE),
) ;
Example: TESTOWNER | SAMPLE_TABLE | SPECIAL_COLUMN = 123
Now the program loops (on DEST) over the SRC_TRANSFER_DATA and constructs a MERGE statement. In order to do this it also looks in the DEST_TAB_COLUMNS table if there is a Rule for this table&column. If there is a rule I add the bindvariable to my collection :
l_hostvariable_map(':p'||l_hostvar_cnt) := r_col.operation_function;
At the end I will look over this collection to make the binds. The final Merge (in short) could look like this:
MERGE INTO dest_table dest
USING
(SELECT table_column FROM src_table WHERE special_column= :p1)
src ON
(dest.special_column= :p2)
WHEN matched
THEN UPDATE SET
dest.column1=src.column1,dest.column2= :p3,dest.column3= :p4
WHEN NOT matched
THEN INSERT
(dest.column1,dest.column2,dest.column3)
VALUES
(src.column1,:p5,:p6)
Some of the :pX are a "function". Like in the example before the edit.
I hope this makes it clearer and not more complicated ;)
Check BIND_VARIABLE documentation :
Notice that BIND_VARIABLE is overloaded to accept different Datatype.
So, your code should look like this:
example1 DATE := SYSDATE;
example2 varchar2(300) := NULL;
example3 varchar2(30) := USER;
example4 varchar2(300) := 'Just some Text';
In case you use Execute Immediate
, better use
Execute Immediate 'UPDATE table_name SET column = :a' USING example1;
btw, in earlier Oracle releases (ie Oracle 10) there was indeed a performance difference in using Execute Immediate
or the DBMS_SQL
package. Usually DBMS_SQL
was faster. However, in current releases I don't get any performance difference anymore when I compare them. Of course you will get similar performance only if you use bind-variables in any case.
Also note, using bind-variables are in 99.9% faster that static code - use them whenever possible. It is also beneficial in terms of SQL-Injection and quoting issues.
Update:
Based on your input your procedure may look like this one:
Declare
val_date date;
var_varchar varchar2(3000);
var_number number;
cursor_name INTEGER;
rows_processed INTEGER;
BEGIN
for aCol in (select * from DEST_TAB_COLUMNS) loop
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, 'UPDATE '||aCol.table_name||' SET '||aCol.COLUMN_NAME||' = :val', DBMS_SQL.NATIVE);
if aCol.DATA_TYPE = 'DATE' then
execute immediate 'begin :res := '||aCol.OPERATION_FUNCTION||'; end;' using out val_date;
DBMS_SQL.BIND_VARIABLE(cursor_name, ':x', val_date);
elsif aCol.DATA_TYPE = 'VARCHAR2' then
execute immediate 'begin :res := '||aCol.OPERATION_FUNCTION||'; end;' using out val_varchar;
DBMS_SQL.BIND_VARIABLE(cursor_name, ':x', val_varchar);
elsif aCol.DATA_TYPE = 'NUMBER' then
execute immediate 'begin :res := '||aCol.OPERATION_FUNCTION||'; end;' using out val_number;
DBMS_SQL.BIND_VARIABLE(cursor_name, ':x', val_number);
end if;
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
end loop;
end;
/
Of course, the procedure as above would be terribly slow because you process column-by-column and row-by-row. Anyway, I assume you get an idea how your code can look like. You function may also return not only a single value but several values in a PL/SQL table.
Where your example goes wrong is it tries to pass the text string 'sysdate' instead of a date value. Bind variables are for passing values, not constructing query text.
declare
example1 date := sysdate; -- an actual date value, not the word 'sysdate'!
cursor_id integer;
rows_processed integer;
begin
cursor_id := dbms_sql.open_cursor;
dbms_sql.parse(cursor_id, 'update demo set dt = :x', dbms_sql.native);
dbms_sql.bind_variable(cursor_id, 'x', example1);
rows_processed := dbms_sql.execute(cursor_id);
dbms_sql.close_cursor(cursor_id);
end;
(The second argument to dbms_sql.bind_variable
is name
, so I pass 'x'
not ':x'
, although it appears to accept either.)
As Wernfried has already pointed out, you don't need all of the complexity of DBMS_SQL for this, as you could just use execute immediate
:
declare
example1 date := sysdate;
begin
execute immediate 'update demo set dt = :x' using example1;
end;
As I undestand you want to replace some "binds" with function. And it functions should be executed in run time for example:
execute immediate 'UPDATE TEST_TABLE
SET a = :bind_function'
using 'substr(a,1,10)';
should be executed as :
UPDATE TEST_TABLE
SET a = substr(a,1,10);
Oracle bind vriables are not allow to do it; But you may write your function to replace placeholders
declare
l_sql varchar2(4000);
l_placeholder1 varchar2(4000) := '(\w+)\s+/\*placeholder_1\*/';
l_function1 varchar2(4000) := 'substr(\1,1,10)';
l_placeholder2 varchar2(4000) := '(\w+)\s+/\*placeholder_2\*/';
l_function2 varchar2(4000) := 'nvl(\1,1000)';
begin
l_sql := 'UPDATE TEST_TABLE
SET column_a = column_a /*placeholder_1*/
, column_b = column_b /*placeholder_2*/
, column_c = column_e /*placeholder_1*/
where column_d /*placeholder_2*/ = 10';
l_sql := REGEXP_REPLACE(l_sql,l_placeholder1,l_function1);
l_sql := REGEXP_REPLACE(l_sql,l_placeholder2,l_function2);
dbms_output.put_line(l_sql);
execute immediate l_sql;
end;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.