【问题标题】:dynamic sql, bind variables and dynamic USING动态 sql、绑定变量和动态 USING
【发布时间】:2018-05-09 12:47:12
【问题描述】:

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

附言。我知道我可以在每个占位符中插入空值,但这意味着当我向表中添加一个字段时,我必须更新该过程,这不是可行的方法。

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;

【问题讨论】:

  • 您的insert into tbl_users 总是可以为所有列使用占位符,如果没有值,只需将NULL 作为绑定参数传递
  • @KaushikNayak 我确定您在我添加编辑时发表了评论。请参阅 OP 中的 p.s. 行。
  • “我正在尝试构建动态 sql 语句,但字符串可能超过 4000 个字符” - 为什么不使用 CLOB 而不是 VARCHAR2?另请阅读此oracle-base.com/articles/10g/dbms_assert_10gR2
  • @KaushikNayak 即使使用 varchar2(32000) 我仍然得到string literal too long 这是同样的问题在这里演示dba-oracle.com/t_execute_immediate_large_insert_string.htm
  • 如果您将它们作为字符串文字传递,那么您需要将每个单独的块限制为

标签: oracle plsql oracle11g


【解决方案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 的情况下引入新的绑定变量),您将不得不遵循第二个选项。否则,即如果需要修改表结构以获取新的绑定变量并且更改频率较低,我更喜欢第一个选项。

【讨论】:

    猜你喜欢
    • 2015-01-18
    • 2011-04-06
    • 1970-01-01
    • 1970-01-01
    • 2018-02-26
    • 2018-10-18
    • 1970-01-01
    • 2015-01-01
    • 2013-07-08
    相关资源
    最近更新 更多