【问题标题】:Executing a dynamic sql statement into a SYS_REFCURSOR在 SYS_REFCURSOR 中执行动态 sql 语句
【发布时间】:2010-02-09 16:01:38
【问题描述】:

是否可以在 plsql 中执行一条动态 sql 并将结果返回到 sys_refcursor 中?到目前为止,我已经粘贴了我的尝试,但是没有接缝正在工作,这是我通过我的 java 应用程序遇到的错误

ORA-01006: 绑定变量没有 存在 ORA-06512: 在 “LIVEFIS.ERC_REPORT_PK”,第 116 行 ORA-06512: 在第 1 行

但这可能是java误解的东西,一切都很好编译所以我不确定。

 procedure all_carers_param_dy (pPostcode in carer.postcode%type, pAge Number
                                ,pReport out SYS_REFCURSOR) is
  begin
    declare
      lsql  varchar2(500) :='SELECT c.id FROM carer c, cared_for cf,carer_cared_for ccf '
          ||' where c.id = ccf.carer_id (+)'
          ||' AND cf.id (+) = ccf.cared_for_id';

    begin

     if pPostcode is not null and pAge <= 0 then
        lsql := lsql||' AND c.postcode like ''%''|| upper(pPostcode)||''%''';
      elsif pPostcode is null and pAge > 0 then 
         lsql := lsql||' AND ROUND((MONTHS_BETWEEN(sysdate,c.date_of_birth)/12)) = pAge';
      elsif pPostcode is not null and pAge > 0 then
         lsql := lsql ||' AND ROUND((MONTHS_BETWEEN(sysdate,c.date_of_birth)/12)) = pAge'
                      ||' AND c.postcode like ''%''|| upper(pPostcode)||''%''';
      end if;


        execute immediate lsql
        into pReport;


    end;
  end;

我是 plsql 的新手,甚至是动态 sql 的新手,所以任何帮助/建议都将不胜感激。

再次感谢

乔恩

【问题讨论】:

    标签: sql oracle plsql


    【解决方案1】:

    您必须绑定参数pAgepPostcode。在动态 SQL 中,您可以在它们前面加上一个冒号 (:)。如果您使用EXECUTE IMMEDIATEOPEN ... FOR,您将通过位置绑定您的参数,这就是我在示例中将它们重命名为:P1 和:P2 的原因:

    DECLARE
       lsql VARCHAR2(500) := 'SELECT c.id 
                                FROM carer c, cared_for cf, carer_cared_for ccf 
                               WHERE c.id = ccf.carer_id (+)
                                 AND cf.id (+) = ccf.cared_for_id';
    BEGIN
       IF pPostcode IS NULL THEN
          lsql := lsql || ' AND :P1 IS NULL';
       ELSE
          lsql := lsql || ' AND c.postcode like ''%''|| upper(:P1)||''%''';
       IF pPostcode pAge > 0 THEN
          lsql := lsql || ' AND :P2 = ROUND((MONTHS_BETWEEN(sysdate,
                                                            c.date_of_birth)/12))';
       ELSE
          lsql := lsql || ' AND nvl(:P2, -1) <= 0';
       END IF;
       OPEN pReport FOR lsql USING pPostcode, pAge;
    END;
    

    注意:绑定变量的数量和位置必须在编译时知道,这就是为什么我经常使用上面的构造(即使不使用参数也要添加到它的位置) .向查询添加重言式(如AND :P1 IS NULL)不会影响其解释计划。

    【讨论】:

    • +1:CONTEXT 变量在这种情况下更适应
    • 如何在没有 USING 子句的情况下绑定变量(例如:在可选参数的情况下)?我已经阅读了有关 DBMS_SQL.BIND_VARIABLE 的信息,但是当我尝试使用它时,似乎一切都需要重写。你知道怎么做吗?
    • 对于EXECUTE IMMEDIATEOPEN ... FOR,必须在编译时知道变量的数量及其位置,因此为了使用可选参数,您必须使用本示例中所示的解决方法.您可以使用DBMS_SQL 进行动态查询而不受此限制,例如,请参阅This SO answer。您不能将常规动态 SQL (EXECUTE IMMEDIATE) 与 DBMS_SQL 混合使用,因为合成器完全不同。
    • 嗨@VincentMalgrat 当您需要多次使用同一个绑定变量时,我找不到正确的语法。您可以在此处查看 EXECUTE IMMEDIATE 的语法(请参阅“将重复占位符与动态 SQL 一起使用”)docs.oracle.com/cd/B12037_01/appdev.101/b10807/…,但不适用于 OPEN...FOR。它的语法是否与重复的占位符不同?
    • @JDor:AFAIK,EXECUTE IMMEDIATEOPEN...FOR 的语法相同:绑定是按位置而不是名称。您需要在重复的情况下重复变量,因为所有变量都被认为是独立的。例如:OPEN cursor FOR 'SELECT :x, :x, :y FROM DUAL' USING px, px, py;
    【解决方案2】:

    您不能通过使用立即执行来分配引用。

    您必须将 SQL 构建成一个字符串,然后使用 open。

    sql_str := 'SELECT * FROM...';
    open pReport for sql_str;
    

    【讨论】:

    • 从技术上讲,如果 SELECT 返回游标数据类型,您可以将其选择为引用游标(执行立即或静态 sql)。但是您是正确的,因为此选择返回的是 id 值,而不是引用光标。
    【解决方案3】:

    使用 OPEN FOR 语法和绑定变量。

    procedure all_carers_param_dy (pPostcode in carer.postcode%type, pAge Number
                                ,pReport out SYS_REFCURSOR) 
    is
      lsql  varchar2(500) :='SELECT c.id FROM carer c, cared_for cf,carer_cared_for ccf '
          ||' where c.id = ccf.carer_id (+)'
          ||' AND cf.id (+) = ccf.cared_for_id';
    
    begin
    
     if pPostcode is not null and pAge <= 0 then
        lsql := lsql||' AND c.postcode like upper(''%''||:1||''%'')';
        open pReport for lsql using pPostcode;
      elsif pPostcode is null and pAge > 0 then 
         lsql := lsql||' AND ROUND((MONTHS_BETWEEN(sysdate,c.date_of_birth)/12)) = :1';
        open pReport for lsql using pAge;
      elsif pPostcode is not null and pAge > 0 then
         lsql := lsql ||' AND ROUND((MONTHS_BETWEEN(sysdate,c.date_of_birth)/12)) = :1'
                      ||' AND c.postcode like upper(''%''||:2||''%'')';
        open pReport for lsql using pAge, pPostcode;
      end if;
    
    end all_carers_param_dy;
    /
    

    动态 SQL 很难,很难理解,也很难做到正确。棘手的领域之一是处理重复。将 bolierplate 的重复部分声明为常量是一个好主意。另外,请注意,我们可以将大字符串拆分为多行,而不必将它们与'||' 连接起来。这减少了维护开销。

    create or replace procedure all_carers_param_dy 
        (pPostcode in carer.postcode%type
          , pAge Number 
          , pReport out SYS_REFCURSOR)  
    is 
      lsql varchar2(500) ;
    
      root_string constant varchar2(500) :='SELECT c.id FROM carer c
                                    , cared_for cf,carer_cared_for ccf   
                          where c.id = ccf.carer_id (+)  
                          and cf.id (+) = ccf.cared_for_id'; 
      pc_string constant varchar2(256) := 
          ' AND c.postcode like upper(''%''||:pc||''%'')';
      age_string constant varchar2(256) := 
          ' AND ROUND((MONTHS_BETWEEN(sysdate,c.date_of_birth)/12)) = :age';
    begin 
    
     if pPostcode is not null and pAge <= 0 then 
        lsql := root_string || pc_string; 
        open pReport for lsql using pPostcode; 
    
      elsif pPostcode is null and pAge > 0 then  
         lsql := root_string || age_string; 
        open pReport for lsql using pAge; 
    
      elsif pPostcode is not null and pAge > 0 then 
         lsql := root_string || age_string 
                             || pc_string; 
        open pReport for lsql using pAge, pPostcode; 
    
      end if; 
    end all_carers_param_dy; 
    / 
    

    【讨论】:

      【解决方案4】:

      是的,这是可能的。这样做:

      v_sql := 'BEGIN OPEN :1 FOR :2 USING ';
      v_bindvars := pPostcode ||', '||pAge; --this part you can create dynamically base on your if's
      v_sql := v_sql||v_bindvars||' ; END;';
      v_select := 'select yourdata from dual where 1 = :bind_first_var and 2 = :bind_second_var';
      
      execute immediate v_sql using pReport, v_select; 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-05-27
        • 2011-07-02
        • 2011-03-12
        • 1970-01-01
        • 2015-07-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多