2016-10-07 19 views
0

我想我的問題是最好的一個例子說明:DBMS_SQL.BIND_VAR一個函數(例如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,而不是被「翻譯」。

我以前使用Execute Immediate,但由於性能優化,必須切換到DBMS_SQL。隨着執行即時固然有沒有問題,如果你使用這種方法:

Execute Immediate 'UPDATE table_name SET column = ' || example1; 

但我不能想辦法與BIND_VARIABLE到archieve這一點。

(當然我可以在解析的語句Concat的變量像執行立即,但我想我會失去性能這樣的性能是非常重要的在這種情況下)。


編輯:

一個例子,更接近現實是:從一個DB(SRC)

將數據複製到另一個數據庫(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 | REPLACE | 'null'

SRC_OWNER_NAME | SAMPLE_TABLE | SAMPLE_COL2 | DATE | REPLACE | 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

現在程序通過SRC_TRANSFER_DATA循環(在DEST上)並構造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被一個「功能」。就像編輯前的例子一樣。

我希望這是更清楚,而不是更復雜;)

+0

澄清:這裏的示例變量實際上是表中的字段。這就是爲什麼它的「sysdate」作爲一個字符串,而不是var:= sysdate;因爲該表具有應該爲變量調用什麼「函數」的信息。我必須使用DBMS_SQL,因爲我們有一個動態的綁定變量數量 – SebastianK

+0

我不明白,你能否提供更接近你的** real **代碼的較少「虛構」的例子。 'SYSDATE','USER'或'NULL'肯定不是任何表的列名。 –

+0

@WernfriedDomscheit完成。我希望它可以幫助 – SebastianK

回答

1

檢查BIND_VARIABLE documentation

注意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版本(即甲骨文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表中的單個值而且還返回多個值。

+0

感謝您的答案。請閱讀我在問題中添加的評論。 – SebastianK

0

您的示例出錯的地方是它會嘗試傳遞文本字符串「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_variablename,所以我通過'x'':x',雖然它似乎能夠接受。)

正如Wernfried已經指出的那樣,你並不需要所有的DBMS_SQL的這種複雜性,你可以只使用execute immediate

declare 
    example1 date := sysdate; 
begin 
    execute immediate 'update demo set dt = :x' using example1; 
end; 
+0

感謝你。但請閱讀我在問題中添加的評論。看來我的例子太簡單了:D – SebastianK

0

正如我已瞭解你要替換一些「結合」帶功能。它的功能應該在運行時間來執行。例如:

execute immediate 'UPDATE TEST_TABLE 
    SET a = :bind_function' 
    using 'substr(a,1,10)'; 

應執行:

UPDATE TEST_TABLE 
    SET a = substr(a,1,10); 

甲骨文綁定vriables都不允許這樣做; 但是你可以寫你的函數來替換佔位符

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; 
相關問題