Error(11,10): PLS-00306: wrong number or types of arguments in call to 'CONSTRUCT' - oracle

I am new to plsql i try to run the piece of code but it is giving error and not able to debug
create or replace type final as object ( ename1 varchar2(10), sal1
NUMBER(7,2));--object
create or replace type construct is table of final; /*nested table of
object type */
create or replace function returnmore (empno1 number) /*Function to
return more*/
return construct
AS
vemp construct:=construct();
vename varchar2(10);
vsal1 NUMBER(7,2);
begin
select ENAME,sal into vename,vsal1 from emp where empno=empno1;
vemp.extend;
vemp(1):=construct(vename,vsal1);
return vemp;
end;
But gives me an error
Function SYSTEM.RETURNMORE#loacaDB
Error(11,1): PL/SQL: Statement ignored
Error(11,10): PLS-00306: wrong number or types of arguments in call to 'CONSTRUCT'
I am using oracle10gxe and sqldeveloper4.2

You can prefer using non-preserved keyword such as typ_emp instead of final which's reserved.
SQL> create or replace type typ_emp as object ( ename1 varchar2(10), sal1 number(7,2));
SQL> create or replace type construct is table of typ_emp;
and you can convert your function as below :
create or replace function returnmore( empno1 emp.empno%type )
return construct AS
vemp construct := construct();
vename varchar2(10);
vsal1 number(7, 2);
begin
select ename, sal into vename, vsal1 from emp where empno = empno1;
vemp.extend;
vemp(1) := typ_emp(vename, vsal1);
dbms_output.put_line(vemp(1).ename1);
return vemp;
end;
/
or another way to handle the same operation :
SQL> create or replace function returnmore( empno1 emp.empno%type )
return construct AS
vemp construct := construct();
v_sql varchar2(2000);
begin
v_sql := 'select typ_emp(ename, sal) from emp where empno = :v_empno1';
execute immediate v_sql bulk collect into vemp using empno1;
dbms_output.put_line(vemp(1).ename1);
return vemp;
end;
/
and test by invoking
SQL> set serveroutput on;
SQL> declare
result construct;
begin
result := returnmore( 1 ); -- 1 is just an ordinary presumed value for empno
end;
/ --> this will return the employee name as printed.
P.S. Never ever use SYSTEM user for non-administrative purposes. May be extremely harmful for your database.

Related

PL/SQL Table Function - How to return an empty result set?

I'm trying to create a PL/SQL table function. I don't mind if it is PIPELINED or not. I just want it to return a query-able result set.
And I want to start with an empty result set. (Because it is possible that the result set I intend to construct will be empty.)
Is it possible to create a PL/SQL table function that returns zero rows?
In the books I have, and tutorials I can find, I only see examples that must return at least one record.
Example of the problem:
CREATE OR REPLACE PACKAGE z_util AS
TYPE t_row
IS RECORD (
CATEGORY VARCHAR2( 128 CHAR )
, MEASURE NUMBER
);
TYPE t_tab
IS TABLE OF t_row;
FUNCTION f_test
RETURN t_tab;
END z_util;
/
CREATE OR REPLACE PACKAGE BODY z_util AS
FUNCTION f_test
RETURN t_tab IS
retval t_tab;
BEGIN
RETURN retval;
END;
END z_util;
/
SELECT test.*
FROM TABLE ( z_util.f_test ) test;
Output:
Error starting at line : 24 in command -
SELECT test.*
FROM TABLE ( z_util.f_test ) test
Error at Command Line : 25 Column : 14
Error report -
SQL Error: ORA-00902: invalid datatype
00902. 00000 - "invalid datatype"
*Cause:
*Action:
Something like this?
SQL> CREATE TYPE t_row AS OBJECT (id NUMBER, name VARCHAR2 (50));
2 /
Type created.
SQL> CREATE TYPE t_tab IS TABLE OF t_row;
2 /
Type created.
SQL> CREATE OR REPLACE FUNCTION f_test
2 RETURN t_tab
3 AS
4 retval t_tab;
5 BEGIN
6 RETURN retval;
7 END;
8 /
Function created.
SQL> SELECT f_test FROM DUAL;
F_TEST(ID, NAME)
--------------------------------------------------------------------
SQL>
Saying that this is too simple and that it doesn't work while in package:
SQL> CREATE OR REPLACE PACKAGE pkg_test
2 AS
3 FUNCTION f_test
4 RETURN t_tab;
5 END;
6 /
Package created.
SQL> CREATE OR REPLACE PACKAGE BODY pkg_test
2 AS
3 FUNCTION f_test
4 RETURN t_tab
5 AS
6 retval t_tab;
7 BEGIN
8 RETURN retval;
9 END f_test;
10 END pkg_test;
11 /
Package body created.
SQL> select pkg_test.f_test from dual;
F_TEST(ID, NAME)
-------------------------------------------------------
SQL>
Works OK as well. Did you, by any chance, declare type within the package? If so, try to create it at SQL level.
See 6.4.6 Querying a Collection:
Note: In SQL contexts, you cannot use a function whose return type was declared in a package specification.
You cannot unnest the result of function, but the variable with the same data type:
create or replace package utils as
type t_row is record (category varchar2 (8), measure number);
type t_tab is table of t_row;
function passon (t t_tab:=null) return t_tab;
end utils;
/
create or replace package body utils as
function passon (t t_tab:=null) return t_tab is
begin
return t;
end;
end utils;
/
Usage and outcomes:
var rc refcursor
declare
tab1 utils.t_tab := utils.passon (); -- empty
tab2 utils.t_tab := utils.passon (utils.t_tab (utils.t_row ('category', 50)));
begin
open :rc for
select * from table (tab1) union all
select * from table (tab2);
end;
/
CATEGORY MEASURE
-------- ----------
category 50
jsut use a return statment without any parameters.
I have already one table function that splits a string based in token.
I modified the code for you as an example answet for your quetion.
If you pass the first string null, the function will return no rows, otherwise will return each token in separate row based on the second parameter offcurse.
// create needed types
// row object
CREATE OR REPLACE TYPE T_TOKEN_ROW AS OBJECT (
id NUMBER,
token_text VARCHAR2(50)
);
// table object
CREATE OR REPLACE TYPE T_TOKEN_TAB IS TABLE OF t_token_row;
// create table function to toknize a string
// input : P_string : the string to be toknized
// P_separator : a character to separate tokens
// Outputs : each token in separate record with id field
CREATE OR REPLACE FUNCTION PIPE_tokens (P_string varchar2,P_separator char) RETURN t_token_tab PIPELINED
AS
sLine VARCHAR2(2000);
nPos INTEGER;
nPosOld INTEGER;
nIndex INTEGER;
nLength INTEGER;
nCnt INTEGER;
sToken VARCHAR2(200);
BEGIN
if (P_string is null ) then
return ;
else
sLine := P_string;
IF (SUBSTR(sLine, LENGTH(sLine), 1) <> '|') THEN
sLine := sLine || '|';
END IF;
nPos := 0;
sToken := '';
nLength := LENGTH(sLine);
nCnt := 0;
FOR nIndex IN 1..nLength LOOP
IF ((SUBSTR(sLine, nIndex, 1) = P_separator) OR (nIndex = nLength)) THEN
nPosOld := nPos;
nPos := nIndex;
nCnt := nCnt + 1;
sToken := SUBSTR(sLine, nPosOld + 1, nPos - nPosOld - 1);
PIPE ROW(t_token_row(nCnt,sToken));
--tTokenTab(nCnt) := sToken;
END IF;
END LOOP;
RETURN;
end if;
END;
// 2 Test query
select * from table(PIPE_tokens(null,';')) ;
select * from table(PIPE_tokens('5;2;3',';')) ;

PLSQL Getting expression is of wrong type

I'm a newbee to PLSQL and I'm facing issues while created PLSQL function.It says expression is of wrong type.I need some help.
Here is my function
CREATE OR REPLACE TYPE HOTGROUPTYPE AS OBJECT (
IMEI VARCHAR2(255),
LACCI VARCHAR2(255),
FRAUD_TYPE VARCHAR2(255),
MSISDN VARCHAR2(255)
);
/
CREATE OR REPLACE TYPE HOTGROUPTYPE_tab IS TABLE OF HOTGROUPTYPE;
/
CREATE OR REPLACE FUNCTION get_tab_tf (p_count IN NUMBER, p_days IN NUMBER) RETURN HOTGROUPTYPE_tab
AS
l_tab HOTGROUPTYPE_tab:=HOTGROUPTYPE_tab();
BEGIN
for i in (select IMEI,LACCI,FRAUD_TYPE,MSISDN
from fms_fraud_master_tbl
where request_type='BARRING'
and rownum<2)
loop
l_tab.extend;
l_tab(l_tab.last) := i.IMEI;
end loop;
RETURN l_tab;
END;
/
And I'm getting this error when the execute the function
11/3 PL/SQL: Statement ignored
11/28 PLS-00382: expression is of wrong type
You are not assigning the values to your table type properly. l_tab(<index>) must be assigned the variable of type HOTGROUPTYPE.
You must use this:
l_tab(l_tab.last) := HOTGROUPTYPE(i.IMEI, i.LACCI,i.FRAUD_TYPE,i.MSISDN );
The neatest way to populate a collection from a query is to use bulk collect:
CREATE OR REPLACE FUNCTION get_tab_tf (p_count IN NUMBER, p_days IN NUMBER) RETURN HOTGROUPTYPE_tab
AS
l_tab HOTGROUPTYPE_tab:=HOTGROUPTYPE_tab();
BEGIN
select HOTGROUPTYPE( IMEI,LACCI,FRAUD_TYPE,MSISDN )
bulk collect into l_tab
from fms_fraud_master_tbl
where request_type='BARRING' ;
RETURN l_tab;
END;
/

how to look up another field in Oracle Function

Table 1
ID
----------
1
2
3
4
5
Table 2
ID Desc
------------------------------
A1 Apple
A2 Pear
A3 Orange
I am trying to create a Function in Oracle, so that it add the prefix 'A' in Table 1, and after that I want to look up in Table 2 to get the DESC returned. It has to be a function.
Thank you!!!
You may use the following for creation of such a function :
Create or Replace Function Get_Fruit( i_id table2.description%type )
Return table2.description%type Is
o_desc table2.description%type;
Begin
for c in ( select description from table2 where id = 'A'||to_char(i_id) )
loop
o_desc := c.description;
end loop;
return o_desc;
End;
where
no need to include exception handling, because of using cursor
instead of select into clause.
using table_name.col_name%type for declaration of data types for
arguments or variables makes the related data type of the columns
dynamic. i.e. those would be able to depend on the data type of the
related columns.
the reserved keywords such as desc can not be used as column names
of tables, unless they're expressed in double quotes ("desc")
To call that function, the following might be preferred :
SQL> set serveroutput on
SQL> declare
2 i_id pls_integer := 1;
3 o_fruit varchar2(55);
4 begin
5 o_fruit := get_fruit( i_id );
6 dbms_output.put_line( o_fruit );
7 end;
8 /
Apple
PL/SQL procedure successfully completed
I am not sure with your question- Are you trying to achieve something like this:-
CREATE OR REPLACE FUNCTION Replace_Value
(
input_ID IN VARCHAR2
) RETURN VARCHAR2
AS
v_ID varchar(2);
BEGIN
begin
SELECT distinct a.ID into v_id from Table 2 a where a.ID in (select 'A'||b.id from table1 b where b.id=input_ID);
exception
when others then
dbms_output.put_line(sqlcode);
end;
RETURN v_id;
END Replace_Value;
Are you trying for something like this?
CREATE OR replace FUNCTION replace_value (table_name IN VARCHAR2,
input_id IN INTEGER)
RETURN VARCHAR2
AS
v_desc VARCHAR(20);
BEGIN
SELECT descr
INTO v_desc
FROM table2
WHERE id = 'A' || input_id
AND ROWNUM = 1; -- only needed if there are multiple rows for each id.
RETURN v_desc;
END replace_value;
You may also add an exception handling for NO_DATA_FOUND or INVALID_NUMBER

How to run Oracle function which returns more than one value

My test function is this
CREATE OR REPLACE FUNCTION MULTI_VAL
(MYNAME OUT EMP2017.ENAME%TYPE)
RETURN NUMBER AS
MYSAL EMP2017.SAL%TYPE;
BEGIN
SELECT SAL, ENAME INTO MYSAL, MYNAME FROM EMP2017 ;
RETURN MYSAL;
END;
/
When I run it like
variable mynm varchar2(20)
SELECT MULTI_VAL(:mynm) FROM dual;
it gives this error
ERROR at line 1:
ORA-06553: PLS-561: character set mismatch on value for parameter 'MYNAME'
The error you get now indicates a datatype mismatch.
However there is a fundamental problem with your code. We cannot use functions which have OUT parameters in SQL. So once you have fixed the datatype issue you will get this error: ORA-06572: Function MULTI_VAL has out arguments.
You can run it like this:
declare
n varchar2(20);
x number;
begin
x := multi_val(n);
end;
/
Generally, functions with OUT parameters are considered bad practice. The syntax allows them, but the usage is hard to understand. It's better to use a procedure with two OUT parameters (because we can only call the program in PL/SQL anyway) or else have the function return a user-defined type.
CREATE TABLE EMP2017(ENAME VARCHAR2(10),SAL NUMBER);
INSERT INTO EMP2017 VALUES ('SMITH',5000);
INSERT INTO EMP2017 VALUES ('JOHNS',1000);
COMMIT;
CREATE TYPE RET_MULT AS OBJECT
(ENAME VARCHAR2(10),SAL NUMBER);
CREATE TYPE T_RET_MULT AS TABLE OF RET_MULT;
CREATE OR REPLACE FUNCTION MULTI_VAL RETURN T_RET_MULT PIPELINED IS
MYSAL RET_MULT;
BEGIN
FOR I IN(SELECT SAL, ENAME FROM EMP2017) LOOP
MYSAL := RET_MULT(I.ENAME,I.SAL);
PIPE ROW(MYSAL);
END LOOP ;
RETURN ;
END;
SELECT * FROM TABLE(MULTI_VAL());
I think this question can be solved without using pipeline functions. Just like this. All pre required data as described #Sedat.Turan except function. Sorry for copy/past.
CREATE TABLE EMP2017(ENAME VARCHAR2(10),SAL NUMBER);
INSERT INTO EMP2017 VALUES ('SMITH',5000);
INSERT INTO EMP2017 VALUES ('JOHNS',1000);
COMMIT;
CREATE TYPE RET_MULT AS OBJECT
(ENAME VARCHAR2(10),SAL NUMBER);
CREATE TYPE T_RET_MULT AS TABLE OF RET_MULT;
create or replace function MULTI_VAL return T_RET_MULT is
RET_SET T_RET_MULT;
begin
select RET_MULT(ENAME, SAL) bulk collect into RET_SET from EMP2017;
return RET_SET;
end;

Oracle procedure input is comma delimited, not returning any values

the procedure Im working has an input variable that is comma delimited. As of right now when I go to run a test script, I dont get any values back. Here is what I have so far.
procedure get_patient(
p_statusmnemonic_in in membermedicalreconcilationhdr.reconciliationstatusmnemonic%type,
p_return_cur_out out sys_refcursor,
p_err_code_out out number,
p_err_mesg_out out varchar2)
is
begin
open p_return_cur_out for
select h.primarymemberplanid,
h.assigneduserid,
h.accountorgid,
h.reconciliationstatusmnemonic,
h.estimatedenddt,
h.actualenddt,
h.inserteddt,
h.insertedby,
h.updateddt,
h.updatedby
from membermedicalreconcilationhdr h
where h.reconciliationstatusmnemonic in (p_statusmnemonic_in);
p_err_code_out := 0;
exception
when others then
p_err_code_out := -1;
p_err_mesg_out := 'error in get_patient=> ' || sqlerrm;
end get_patient;
Here is the test script:
set serveroutput on
declare
type tempcursor is ref cursor;
v_cur_result tempcursor;
errcode number;
errmesg varchar2(1000);
p_primarymemberplanid_in membermedicalreconcilationhdr.primarymemberplanid%type;
p_assigneduserid_in membermedicalreconcilationhdr.assigneduserid%type;
p_accountorgid_in membermedicalreconcilationhdr.accountorgid%type;
p_reconstatusmnemonic_in membermedicalreconcilationhdr.reconciliationstatusmnemonic%type;
p_estimatedenddt_in membermedicalreconcilationhdr.estimatedenddt%type;
p_actualenddt_in membermedicalreconcilationhdr.actualenddt%type;
p_inserteddate_in membermedicalreconcilationhdr.inserteddt%type;
p_insertedby_in membermedicalreconcilationhdr.insertedby%type;
p_updateddate_in membermedicalreconcilationhdr.updateddt%type;
p_updatedby_in membermedicalreconcilationhdr.updatedby%type;
begin
get_patient
('COMPLETE,SUSPENDED_PRIOR_TO_COMPARE',v_cur_result, errcode, errmesg);
--('COMPLETE',v_cur_result, errcode, errmesg);
loop
fetch v_cur_result into p_primarymemberplanid_in,p_assigneduserid_in,p_accountorgid_in,p_reconstatusmnemonic_in,
p_estimatedenddt_in,p_actualenddt_in,p_inserteddate_in,p_insertedby_in,
p_updateddate_in,p_updatedby_in;
dbms_output.put_line(' planid '||p_primarymemberplanid_in||' userid '||p_assigneduserid_in);
exit when v_cur_result%notfound;
end loop;
dbms_output.put_line(' error code '||errcode||' message '||errmesg);
end;
As of right now I get values back when I just have one input value, but when I try to do two I dont get anything. Ive done research and it looks like my select statement is correct so Im at a loss as to what Im doing wrong. Any help is appreciated, thanks.
If you can change the definition of the procedure, you are better served passing in a proper collection.
CREATE TYPE status_tbl IS TABLE OF VARCHAR2(100);
procedure get_patient(
p_statusmnemonic_in in status_tbl,
p_return_cur_out out sys_refcursor,
p_err_code_out out number,
p_err_mesg_out out varchar2)
is
begin
open p_return_cur_out for
select h.primarymemberplanid,
h.assigneduserid,
h.accountorgid,
h.reconciliationstatusmnemonic,
h.estimatedenddt,
h.actualenddt,
h.inserteddt,
h.insertedby,
h.updateddt,
h.updatedby
from membermedicalreconcilationhdr h
where h.reconciliationstatusmnemonic in (SELECT *
FROM TABLE(p_statusmnemonic_in));
...
Otherwise, you would either have to resort to using dynamic SQL (which would have security and performance implications) or you would need to write code to parse the comma-separated string into a collection and then use the TABLE operator to use that collection in the query.
Assuming you modify the signature of the procedure, the call will also have to change so that you are passing in a collection.
get_patient
(status_tbl('COMPLETE','SUSPENDED_PRIOR_TO_COMPARE'),
v_cur_result,
errcode,
errmesg);
And just to point it out, writing procedures that have error code and error message OUT parameters rather than throwing exceptions is generally highly frowned upon. It makes far more sense to eliminate those parameters and to just throw exceptions when you encounter an error. Otherwise, you are relying on every caller to every procedure to correctly check the returned status code and message (which your sample code does not do). And you are losing a ton of valuable information about things like exactly what line an error occurred on, what the error stack was, etc.
Since you don't post your table definitions or your sample data, it is impossible for us to test this code. Here is a quick demonstration, though, of how it would work
SQL> create table patient (
2 patient_id number primary key,
3 status varchar2(10),
4 name varchar2(100)
5 );
Table created.
SQL> insert into patient values( 1, 'COMPLETE', 'Justin' );
1 row created.
SQL> insert into patient values( 2, 'SUSPENDED', 'Bob' );
1 row created.
SQL> insert into patient values( 3, 'NEW', 'Kerry' );
1 row created.
SQL> commit;
Commit complete.
SQL> CREATE TYPE status_tbl IS TABLE OF VARCHAR2(100);
2 /
Type created.
SQL> ed
Wrote file afiedt.buf
1 create or replace procedure get_patients( p_statuses in status_tbl,
2 p_cursor out sys_refcursor )
3 as
4 begin
5 open p_cursor
6 for select *
7 from patient
8 where status in (select *
9 from table( p_statuses ));
10* end;
SQL> /
Procedure created.
SQL> variable rc refcursor;
SQL> exec get_patients( status_tbl('COMPLETE', 'SUSPENDED'), :rc );
PL/SQL procedure successfully completed.
SQL> print rc;
PATIENT_ID STATUS
---------- ----------
NAME
--------------------------------------------------------------------------------
1 COMPLETE
Justin
2 SUSPENDED
Bob

Resources