簡體   English   中英

DBMS_SQL.BIND_VAR轉換為函數(例如sysdate)

[英]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 ImmediateDBMS_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM