简体   繁体   English

DBMS_SQL.BIND_VAR转换为函数(例如sysdate)

[英]DBMS_SQL.BIND_VAR to a function (e.g. sysdate)

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". 所有“ exampleX”变量都将被绑定为varchar2,而不是被“翻译”。

I previously used Execute Immediate but had to switch to DBMS_SQL because of performance optimizations. 我以前使用了立即执行,但是由于性能优化而不得不切换到DBMS_SQL。 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. 但是我想不出一种方法来用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) (当然,我可以像执行即时操作一样在PARSE语句中连接变量,但我认为我会以这种方式失去性能。在这种情况下,性能非常重要)


Edit: 编辑:

An example, closer to reality would be: 一个更接近现实的例子是:

Copying data from one DB(SRC) to another DB(DEST), where I have this helper table: 将数据从一个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)
   ) ;

In this Table I define which columns I am interested in on the DEST side. 在此表中,我定义了我在DEST侧感兴趣的列。 And I have the option to define a "OPERATION_FUNCTION" to replace a certain column value. 而且我可以选择定义“ OPERATION_FUNCTION”来替换某个列值。

So an entry would look like: 因此,条目看起来像:

SRC_OWNER_NAME | SRC_OWNER_NAME | SAMPLE_TABLE | SAMPLE_TABLE | SAMPLE_COL | SAMPLE_COL | VARCHAR2 | VARCHAR2 | REPLACE | 更换 'null' '空值'

SRC_OWNER_NAME | SRC_OWNER_NAME | SAMPLE_TABLE | SAMPLE_TABLE | SAMPLE_COL2 | SAMPLE_COL2 | DATE | DATE | REPLACE | 更换 sysdate sysdate

On the SRC side I define which data I want to have transvered. 在SRC端,我定义要转换的数据。 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 | 示例:TESTOWNER SAMPLE_TABLE | SAMPLE_TABLE | SPECIAL_COLUMN = 123 SPECIAL_COLUMN = 123

Now the program loops (on DEST) over the SRC_TRANSFER_DATA and constructs a MERGE statement. 现在,程序(在DEST上)在SRC_TRANSFER_DATA上循环并构造MERGE语句。 In order to do this it also looks in the DEST_TAB_COLUMNS table if there is a Rule for this table&column. 为此,如果该表和列有规则,它也会在DEST_TAB_COLUMNS表中查找。 If there is a rule I add the bindvariable to my collection : 如果有规则,我将bindvariable添加到我的集合中:

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". :pX的某些是“功能”。 Like in the example before the edit. 就像在编辑之前的示例中一样。

I hope this makes it clearer and not more complicated ;) 我希望这可以使它更清晰而不复杂;)

Check BIND_VARIABLE documentation : 查看BIND_VARIABLE文档

Notice that BIND_VARIABLE is overloaded to accept different Datatype. 请注意,BIND_VARIABLE已重载以接受其他数据类型。

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 ,最好使用

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. 顺便说一句,在早期的Oracle版本(即Oracle 10)中,使用Execute ImmediateDBMS_SQL包确实存在性能差异。 Usually DBMS_SQL was faster. 通常, DBMS_SQL速度更快。 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. 还要注意,使用绑定变量比静态代码快99.9%-尽可能使用它们。 It is also beneficial in terms of SQL-Injection and quoting issues. 在SQL注入和引用问题方面,它也是有益的。

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. 您的函数还可能不仅在PL / SQL表中返回单个值,而且还返回几个值。

Where your example goes wrong is it tries to pass the text string 'sysdate' instead of a date value. 您的示例出错的地方是它尝试传递文本字符串'sysdate'而不是日期值。 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.) dbms_sql.bind_variable的第二个参数是name ,因此我传递了'x'而不是':x' ,尽管它似乎接受了任何一个。)

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 : 正如Wernfried所指出的那样,您并不需要DBMS_SQL的所有复杂性,因为您可以使用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; Oracle绑定变量不允许这样做; 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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM