0

I want to create a function that returns multiple varchar2 columns. his is what I have so far, but it errors on the execute immediate call.

FUNCTION FNC_LST_OUR(p_com_code   varchar2,
                     p_from  varchar2,
                     p_to    varchar2                                  
                     ) return VARCHAR2 is                                 

v_sql VARCHAR2(30000) := '';
v_sql2 VARCHAR2(30000) := '';  
error_msg_tmp varchar2(255);

begin  
      v_sql := 'select s.com_code, s.p_code, count(*) as Num_SIMRP    
                from p_stest s                 
                where s.com_code = ''' || p_com_code || '''                                      
                and s.s_status = ''ACTIVE''
                and s.s_type like ''H%'' ';                 
      if p_from is not null then
        v_sql := v_sql || ' and s.p_code between ''' || p_from || ''' and ''' || p_to || '''';
      end if;          
        v_sql := v_sql || ' group by s.com_code,s.p_code
                            having count(s.p_code)> 0 ';

      EXECUTE IMMEDIATE v_sql INTO v_sql2 USING p_com_code; --> Error This Line
      return v_sql2;
end;     

I need it to return multiple columns like:

com_code | p_code | num_simrp
A        | ADSWQ  | 14
A        | AQWSA  | 8
A        | DEWSQ  | 10
A        | SDERS  | 45
A        | DFDEW  | 80

I must create the function in a package and inner join the result as part of a query.

2
  • What is the caller of this function going to do with the returned data? You appear to want to return either a collection or a ref cursor, but which depends on how it will be used. (You don't really need dynamic SQL for this, but that's a separate issue. It's also useful to show what error you get, though it's obvious here.) Commented Jun 8, 2016 at 11:52
  • Why do you "have to" do it like that? Inner joining the result of a function is one of the worst things, you can do. Commented Jun 8, 2016 at 12:03

3 Answers 3

1

The error this code throws is "ORA-01006: bind variable does not exist", because your execute immediate has a using clause but the query is using concatenated values instead of bind variables. It's better to use binds, of course; you're complicating things a bit by changing the query conditionally. But without that it will still error as you're trying to select three values into one string, which doesn't go.

If you want to use the results of this in another query then you will need to return a collection, and (from the inner join reference) it will need to be a SQL schema-level collection type not a PL/SQL one.

create type fnc_list_obj as object (
  com_code varchar2(10),
  p_code varchar2(10),
  num_simrp number)
/

create type fnc_list_tab as table of fnc_list_obj
/

create package p42 as
function FNC_LST_OUR(P_COM_CODE   varchar2,
                     p_from  varchar2,
                     p_to    varchar2                                  
                     ) return fnc_list_tab;
end p42;
/

You don't need to use dynamic SQL. You can either check if p_from is null and decide which complete SQL query to run, or more simply include that check within a single query:

create package body p42 as
function fnc_lst_our(p_com_code varchar2,
                     p_from varchar2,
                     p_to varchar2
                     ) return fnc_list_tab is

      v_fnc_list fnc_list_tab;
begin
      select fnc_list_obj(s.com_code, s.p_code, count(*))
      bulk collect into v_fnc_list
      from p_stest s
      where s.com_code = p_com_code
      and s.s_status = 'ACTIVE'
      and s.s_type like 'H%'
      and (p_from is null or s.p_code between p_from and p_to)
      group by s.com_code,s.p_code
      having count(s.p_code)> 0;

      return v_fnc_list;
end;
end p42;
/

The having clause may be redundant, but at the moment it excludes rows where all the p_code values are null, which may be a more obvious way to check if that's the intenstion.

You can see what that finds by calling it directly:

select * from table(p42.fnc_lst_our('A', null, null));

.. and can use the same table collection expression, with a table alias, to join to other tables.

Depending on the amount of data you might want to make it a pipelined function instead. It may be simpler and more efficient to just include the logic and that table in your bigger query as an inline view or CTE.

Sign up to request clarification or add additional context in comments.

1 Comment

it's error lone :type fnc_list_tab as table of fnc_list_obj PL-00103 .."TABLE"...
0

Forget the whole function, you can just use a subquery. I guess you want something like this:

select *
from a_table a
join (select s.com_code, s.p_code, count(*) as Num_SIMRP    
      from p_stest s                 
      where s.s_status = 'ACTIVE'
        and s.s_type like 'H%'
      group by s.com_code,s.p_code
      having count(s.p_code) > 0) b on b.com_code = a.com_code 
                                    and (p_from is null or s.p_code between p_from and p_to)

1 Comment

always try to solve such problems in SQL first. Only if that doesn't work or get's too complicated, make a function. Using pure SQL is (most of the times) much faster.
0
create or replace type  test_type as object(t_owner varchar2(2000),t_originator varchar2(2000),
t_modify_dat timestamp,t_create_date  timestamp);

create or replace type  test_table is table of test_type;

create or replace function test_fn(table_name varchar2)
return test_table
as
test_var test_table:=test_table();
t_owner varchar2(2000);
t_originator varchar2(2000);
t_modify_date timestamp;
t_create_date timestamp;
begin

      execute immediate 'select  "OWNER","ORIGINATOR","MODIFIED","ORIGINATED"   from '|| table_name ||'
           where  revision = 1 and "TYPE" like ''gecPowder'' and "NAME" like ''GE2009-0653'''
           into t_owner,t_originator,t_modify_date,t_create_date;
           test_var.extend();
           --DBMS_OUTPUT.PUT_line(t_owner||t_originator||t_modify_date||t_create_date);
           select test_type(t_owner,t_originator,t_modify_date,t_create_date) bulk collect into test_var from dual;
           return test_var;
           end;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.