[英]DBMS_SQL.BIND_VAR to a function (e.g. sysdate)
我认为我的问题最好用一个例子来描述:
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;
/
所有“ exampleX”变量都将被绑定为varchar2,而不是被“翻译”。
我以前使用了立即执行,但是由于性能优化而不得不切换到DBMS_SQL。 如果使用这种方法,使用立即执行当然不会有问题:
Execute Immediate 'UPDATE table_name SET column = ' || example1;
但是我想不出一种方法来用BIND_VARIABLE归档。
(当然,我可以像执行即时操作一样在PARSE语句中连接变量,但我认为我会以这种方式失去性能。在这种情况下,性能非常重要)
编辑:
一个更接近现实的例子是:
将数据从一个DB(SRC)复制到另一个DB(DEST),其中有此帮助程序表:
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)
) ;
在此表中,我定义了我在DEST侧感兴趣的列。 而且我可以选择定义“ OPERATION_FUNCTION”来替换某个列值。
因此,条目看起来像:
SRC_OWNER_NAME | SAMPLE_TABLE | SAMPLE_COL | VARCHAR2 | 更换 '空值'
SRC_OWNER_NAME | SAMPLE_TABLE | SAMPLE_COL2 | DATE | 更换 sysdate
在SRC端,我定义要转换的数据。 这是一个简单的表,基本上像这样:
CREATE TABLE "SRC_TRANSFER_DATA"
( "OWNER" VARCHAR2(30 BYTE),
"TABLE_NAME" VARCHAR2(30 BYTE),
"WHERE_CLAUSE" VARCHAR2(300 BYTE),
) ;
示例:TESTOWNER SAMPLE_TABLE | SPECIAL_COLUMN = 123
现在,程序(在DEST上)在SRC_TRANSFER_DATA上循环并构造MERGE语句。 为此,如果该表和列有规则,它也会在DEST_TAB_COLUMNS表中查找。 如果有规则,我将bindvariable添加到我的集合中:
l_hostvariable_map(':p'||l_hostvar_cnt) := r_col.operation_function;
最后,我将查看此集合以进行绑定。 最终的合并(简而言之)可能如下所示:
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)
:pX的某些是“功能”。 就像在编辑之前的示例中一样。
我希望这可以使它更清晰而不复杂;)
查看BIND_VARIABLE文档 :
请注意,BIND_VARIABLE已重载以接受其他数据类型。
因此,您的代码应如下所示:
example1 DATE := SYSDATE;
example2 varchar2(300) := NULL;
example3 varchar2(30) := USER;
example4 varchar2(300) := 'Just some Text';
如果您使用Execute Immediate
,最好使用
Execute Immediate 'UPDATE table_name SET column = :a' USING example1;
顺便说一句,在早期的Oracle版本(即Oracle 10)中,使用Execute Immediate
或DBMS_SQL
包确实存在性能差异。 通常, DBMS_SQL
速度更快。 但是,在当前版本中,当我比较它们时,性能不再有任何区别。 当然,只有在任何情况下都使用绑定变量,您才能获得类似的性能。
还要注意,使用绑定变量比静态代码快99.9%-尽可能使用它们。 在SQL注入和引用问题方面,它也是有益的。
更新:
根据您的输入,您的过程可能如下所示:
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;
/
当然,上述过程将非常缓慢,因为您要逐列和逐行处理。 无论如何,我假设您已经了解了代码的外观。 您的函数还可能不仅在PL / SQL表中返回单个值,而且还返回几个值。
您的示例出错的地方是它尝试传递文本字符串'sysdate'而不是日期值。 绑定变量用于传递值,而不用于构造查询文本。
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;
( dbms_sql.bind_variable
的第二个参数是name
,因此我传递了'x'
而不是':x'
,尽管它似乎接受了任何一个。)
正如Wernfried所指出的那样,您并不需要DBMS_SQL的所有复杂性,因为您可以使用execute immediate
:
declare
example1 date := sysdate;
begin
execute immediate 'update demo set dt = :x' using example1;
end;
我不理解,您想用功能替换一些“绑定” 。 它的功能应在运行时执行,例如:
execute immediate 'UPDATE TEST_TABLE
SET a = :bind_function'
using 'substr(a,1,10)';
应该执行为:
UPDATE TEST_TABLE
SET a = substr(a,1,10);
Oracle绑定变量不允许这样做; 但是您可以编写函数来替换占位符
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;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.