动态sql,绑定变量和动态USING

时间:2018-05-09 12:47:13

标签: oracle plsql oracle11g

我在.net应用程序中有一些动态表单。根据表单,插入/更新中的字段将不同。我试图构建一个动态的sql语句,但字符串可能超过4000个字符,这对字符串文字不起作用,所以我试图使用绑定变量。由于保存的字段是动态的,我不知道如何处理USING块。以下是我试图做的一个愚蠢的版本。如果有助于解释动态表单,您可以查看我提出的关于动态获取数据的旧问题。 collection of records to out sys_refcursor

P.S。我知道我可以在每个占位符中插入空值,但这意味着我在向表中添加字段时不得不更新程序。

procedure bindTest(oCur out sys_refcursor)

    as 
      vFirst varchar2(50) := 'Joe';
      vMiddle varchar2(50) := 'Vs'
      vLast varchar2(50) := 'Volcano';
      vVars varchar2(50) := 'vFirst, vLast';
      vSql varchar2(1000) := '';
    begin
    -- This form does not use the middle name so there are only 2 bind vars. 
    -- The field exists in the table but not in the web form so it would not be passed to the procedure.
    -- I've included it here to show data I want to ignore. 
    -- vVars includes the list of valid fields to save. 
    -- This would be the sql created by my script. 

    vSql := 'insert into tbl_users (firstName, lastName) values (:a, :b)'; 
    -- depending on the form, vSql might look like 
    ---- 'insert into tbl_users (firstName, middle, lastName) values (:a,:b,:c)'
    ---- 'insert into tbl_users (lastName) values (:a)'
    ---- etc

    execute immediate vSql using {what goes here? or how do I handle this?};
    -- I understand normally it would be `USING vFirst, vLast` but what about when it's dynamic?

    open oCur for
        select 
            ID
          , firstName
          , lastName
        from
          tbl_users
        where 
          rownum = 1
        order by 
          id desc;


end bindTest;

1 个答案:

答案 0 :(得分:1)

简单但静态的解决方案假设有一个已知的绑定变量列表及其数据类型,动态查询只能使用这些绑定变量的子集。

这里有五个VARCHAR绑定变量的示例。您生成此PL / SQL块:

DECLARE
L_VC1 VARCHAR2(4000) := :VC1;
L_VC2 VARCHAR2(4000) := :VC2;
L_VC3 VARCHAR2(4000) := :VC3;
L_VC4 VARCHAR2(4000) := :VC4;
L_VC5 VARCHAR2(4000) := :VC5;
BEGIN
  -- here an statement using L_VC1 up to L_VC5
  -- eg
  INSERT INTO test (vc1,vc2,vc3) values (L_VC1, L_VC2, L_VC3);
END;

并执行它传递完整的值列表(其中一些值为NULL)。

EXECUTE IMMEDIATE my_generated_block USING vc1, vc2, vc3, vc4, vc5;

一个很好的功能是,动态SQL可以多次使用一个绑定变量,而无需扩展USING个参数。

如果出现新变量,当然必须保留。

有什么替代方案?

我认为在绑定变量列表中确实是动态的,你无法用EXECUTE IMMEDIATE来解决这个问题,你必须向DBMS_SQL迈进一步。

这里的想法,没有详细说明如何在PL / SQL中实现它:

DECLARE
    cursor_name INTEGER;
    rows_processed INTEGER;
BEGIN
    cursor_name := dbms_sql.open_cursor;
    DBMS_SQL.PARSE(cursor_name, 'INSERT INTO test (vc1,vc2,vc3) values (:vc1, :vc2, :vc3)',
                   DBMS_SQL.NATIVE);
    -- call in a loop for each BV               
    DBMS_SQL.BIND_VARIABLE(cursor_name, ':vc1', 'x');
    DBMS_SQL.BIND_VARIABLE(cursor_name, ':vc2', 'y');
    DBMS_SQL.BIND_VARIABLE(cursor_name, ':vc3', 'z');
    ---
    rows_processed := DBMS_SQL.EXECUTE(cursor_name);
    DBMS_SQL.CLOSE_CURSOR(cursor_name);
END;
/

您必须在循环中为每个绑定变量名称和值调用DBMS_SQL.BIND_VARIABLE

请注意,我完全忽略了绑定变量的数据类型,这也应该被考虑,并且可能会使解决方案更复杂,但可以解决。

哪种解决方案更可行?

如果您的关系数据库设计是真正的键 - 值(即您可能引入没有DDL的新绑定变量),则必须遵循第二个选项。否则,即如果需要对表结构进行修改以获得新的绑定变量,并且如果更改频率较低,我宁愿选择第一个选项。