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 ;

 

posted @ 2015-11-29 19:08  MorePrograms  阅读(383)  评论(0编辑  收藏  举报