zoukankan      html  css  js  c++  java
  • function dynamic fetch in Oracle/PLSQL

      

      In coding, we often meet some sql queries like ' select many cloumns from tablename where condition; '. When the number of cloumns is dynamic, we should use ResultSet to achieve in Java. As we all know, the class ResultSet has been defined in Java class library. We can use it to get the result of query with optional cloumns directly. However, we are now in Oracle/PLSQL, and if we want to get the same effect as Java, we should get one better way to solve that. You may think about using cursor to achieve you want. Actually, you will realize that's not exactly what you think. There is one solution for what we said, the following function makes full use of cursor to get what we want. And it has been verified on Oracle DB. I really wish this func can help you all.

      

    create or replace function func_dynamic_fetch 
    (   p_sql in varchar2, 
        p_no_of_cols integer,  
        p_return_multiple_row boolean := true)
    return anydatalist is 
        lc_rs sys_refcursor;
        strlist varchar2List := varchar2List();
        n number := 3;
        code varchar2(32767);
        TYPE ref_cursor IS REF CURSOR;
        rc_         ref_cursor;  
        c_          NUMBER;
        i_          NUMBER;
        col_count_  NUMBER;
        desc_tab_   DBMS_SQL.DESC_TAB2;
        col_num  NUMBER;
        n2 number;
        l_any_list anydatalist;
        l_types varchar2list; 
        l_add_stmts varchar2list;
        l_fetch_type varchar2(32767);
        c_date_type constant number := 12;
        c_timestamp_type constant number := 180;
    
    
        function repeatStmt(n in integer, template in varchar2, seperator in varchar2 := '', vals in varchar2list := null) return varchar2 is
            l_result varchar2(32767);
            l_stmt varchar2(32767);
        begin
            for i in 1..n loop
                l_stmt := template;
                if vals is not null then
                    l_stmt := replace(template, '__val__', vals(i)); 
                end if;
                l_result := l_result || replace(l_stmt, '__$__', i); 
                if i < n then
                    l_result := l_result || seperator;
                end if;
            end loop;
            return l_result;
        end repeatStmt;
    begin  
        open rc_ for p_sql;  
        n2 := p_no_of_cols;
        c_ := DBMS_SQL.to_cursor_number(rc_);
        DBMS_SQL.DESCRIBE_COLUMNS2(c_, col_count_, desc_tab_);
        col_num := desc_tab_.first;
        l_types := varchar2list();
        l_add_stmts := varchar2list();
        if col_num is not null then
            loop
                if desc_tab_(col_num).col_type = c_date_type or desc_tab_(col_num).col_type =c_timestamp_type then 
                    varchar2listAdd(l_types, 'date');
                    --to conform to jdbc date format 
                    varchar2listAdd(l_add_stmts, 'varchar2ListAdd(l_str_list, to_char(a__$__, ''yyyy-mm-dd hh:mi:ss''));');
                else 
                    varchar2listAdd(l_types, 'varchar2(32767)');
                    varchar2listAdd(l_add_stmts, 'varchar2ListAdd(l_str_list, a__$__);');
                end if;
                col_num:= desc_tab_.next(col_num);
                exit when (col_num is null);
            end loop;
        end if;
    
        --dbms_output.put_line('cursor count: ' || col_count_);
        n := col_count_; 
        if n2 > n then 
            n2 := n; 
        end if;
        --dbms_output.put_line('number of columns: ' || n );
        --dbms_output.put_line('number of columns to fetch: ' || n2 );
        dbms_sql.close_cursor(c_);
        --if rc_%isopen then 
        --    close rc_;
        --end if;
        code := 
        ' declare
            lc_rs sys_refcursor;
            l_sql varchar2(32767);
            l_str_list varchar2list; 
            l_counter integer;
            l_any_list anydatalist; ' ||  
            repeatStmt(n, 'a__$__ __val__; ',  seperator => chr(10), vals => l_types) || '
          begin
            l_sql := :0;
            --dbms_output.put_line(l_sql);
            open lc_rs for l_sql;
            l_any_list := anydatalist();
            l_counter := 0; 
            loop
                fetch lc_rs into ' || repeatStmt(n, 'a__$__', ', ') || ';' || '
                exit when lc_rs%notfound;
                l_counter := l_counter + 1; 
                l_str_list := varchar2List();' || 
                --repeatStmt(n2, 'varchar2ListAdd(l_str_list, a__$__);', chr(10), vals => l_types) || '
                repeatStmt(n2, '__val__', chr(10), vals => l_add_stmts) || '
                anydataListAdd(l_any_list, anydata.convertCollection(l_str_list)); ' || 
                (case when p_return_multiple_row = false then 
                'exit when l_counter = 1;'
                end) || '
            end loop;
            if lc_rs%isopen then 
                close lc_rs;
            end if;
            :1 := l_any_list;
          exception when others then  
            dbms_output.put_line('' func_dynamic_fetch exec immediate exception: '' || sqlerrm);
            raise;
            if lc_rs%isopen then close lc_rs; end if;
          end;
        ' ; 
        --dbms_output.put_line(code);
        execute immediate code using p_sql, out l_any_list;
        return l_any_list;
    exception when others then 
        if rc_%isopen then close rc_; end if; 
        dbms_output.put_line('func_dynamic_fetch: exception: ' || sqlerrm);
        return anydatalist();
    end;
     /
    show errors;
    

    Test case:

    DECLARE
    
        v_sql          VARCHAR2(1024) := 'select a.code,a.prod_code,a.alloc_qty from uni_allotresultitem a where a.id = 204';
        v_anydatalist  anydatalist;
        v_varchar2list varchar2list := varchar2list();
        v_anydata      anydata;
        v_vnt          NUMBER(10);
    BEGIN
        v_anydatalist := func_dynamic_fetch(v_sql, 2, TRUE);
    
        FOR i IN 1 .. v_anydatalist.count LOOP
            v_anydata := v_anydatalist(i);
            v_vnt := v_anydata.getcollection(v_varchar2list);
            FOR j IN 1 .. v_varchar2list.count LOOP
            
                dbms_output.put_line(i||':v_varchar2list(' || j || '):' ||
                                     v_varchar2list(j));
            END LOOP;
        END LOOP;
    
    END;
    

    Test result:

    Related type:

    CREATE OR REPLACE TYPE AnydataList FORCE IS VARRAY (2147483647) OF ANYDATA  ;
    /
    CREATE OR REPLACE PROCEDURE
    AnydataListAdd (list IN OUT NOCOPY AnydataList , v IN ANYDATA ) IS
    BEGIN list.extend ; list (list.count ) := v ; END ;
    /
    CREATE OR REPLACE TYPE Varchar2List FORCE IS VARRAY (2147483647) OF VARCHAR2 (32767) ;
    /
    CREATE OR REPLACE PROCEDURE
    Varchar2ListAdd (list IN OUT NOCOPY Varchar2List , v IN VARCHAR2 ) IS
    BEGIN list.extend ; list (list.count ) := v ; END ;
    
  • 相关阅读:
    ftp实现普通账号和vip账号限速
    CentOS7 无人值守服务环境搭建(PXE + DHCP+TFTP+ Kickstart+ FTP)
    rsync 系统用户/虚拟用户 备份web服务器数据及无交互定时推送备份
    LVM逻辑卷管理
    Linux允许、禁止ping包
    javascript call 与 apply
    js 内存进阶 function扫描解析
    事件绑定之鼠标悬停 -入门-进阶-精通-骨灰(来自锋利的jQuery)
    show(),hide()和display在一起的用法
    Nat模式下网卡配置及Xshell连接
  • 原文地址:https://www.cnblogs.com/Jeffrey-xu/p/5005204.html
Copyright © 2011-2022 走看看