Expression is of wrong type when sending nested array to Function - plsql

I want to send a collection to a Function, but I keep getting an error.
I defined the RECORD and TYPES in my Package Header and Implemented the body aswell. I dont understand why I cant send a simple collection as a parameter, the idea is for me to loop through the collection and do some comparisation then return a char within a sql statement.
Been struggling with this for a week now, any help is appreciated.
Exact error:
ORA-06550: line 9, column 45:
PLS-00382: expression is of wrong type
ORA-06550: line 9, column 40:
PLS-00306: wrong number or types of arguments in call to 'TEST_F'
ORA-06550: line 9, column 23:
PL/SQL: ORA-00904: "PACKAGE_SS"."TEST_F": invalid identifier
ORA-06550: line 9, column 3:
PL/SQL: SQL Statement ignored
Header:
create or replace
PACKAGE PACKAGE_SS AS
type t_itemnumber is table of varchar2(100) index by BINARY_INTEGER;
type t_alternative_rec is record
(
itemnumber t_itemnumber
);
type t_alternative_prev is table of t_alternative_rec INDEX BY BINARY_INTEGER;
type t_procestype_rec is record
(
procestype char
);
TYPE result_table IS TABLE OF t_procestype_rec;
FUNCTION test_f(p_items_prev IN t_alternative_prev) RETURN result_table PIPELINED;
END AOPA_VALIDATE_SS;
The package body looks like this:
create or replace
PACKAGE BODY PACKAGE_SS AS
FUNCTION test_f(p_items_prev IN t_alternative_prev) RETURN result_table PIPELINED IS
processType char(1) := 'U';
rec t_procestype_rec :=null;
BEGIN
DBMS_OUTPUT.PUT_LINE('ENTERD ');
if (processType= 'U') then
select 'U' into rec from dual;
end if;
if (processType='C') then
select 'C' into rec from dual;
end if;
if (processType='D') then
select 'D' into rec from dual;
end if;
pipe row (rec);
return;
END test_f;
END PACKAGE_SS;
Usage plsql script:
DECLARE
prev_rev_alternatives PACKAGE_SS.t_alternative_prev;
BEGIN
prev_rev_alternatives(1).itemnumber(10) := 'PR454545';
prev_rev_alternatives(1).itemnumber(20) := 'PR333333';
SELECT * FROM table(PACKAGE_SS.test_f(prev_rev_alternatives));
END;

There are two things which my eyes fall upon:
Usage of SELECT without INTO in an anonymous PLSQL block is not supposed to work.
Usage of types in an SQL statement requires the type defined as an object, not within a package specification.
Try
CREATE TYPE
https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_8001.htm
and
SELECT * INTO ... FROM ...
This might help.

Related

Getting error while assigning values to records inside collection

I need to loop through some values inside the plsql code.
so trying to create a collection of records .
getting error as mentioned below.
Please help me to resolve
-- please see the code below
declare
type tab_name is record (t_name varchar2(30),col_name varchar2(30));
type tab_list is table of tab_name;
table_names tab_list:=tab_list(tab_name('ABC','abc'),tab_name('XYZ','xyz'));
begin
for i in table_names.first..table_names.last loop
dbms_output.put_line(table_names(i).t_name||'-'||table_names(i).col_name);
end loop;
end;
/
Error report -
ORA-06550: line 4, column 32:
PLS-00222: no function with name 'TAB_NAME' exists in this scope
ORA-06550: line 4, column 13:
PL/SQL: Item ignored
ORA-06550: line 6, column 10:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
ORA-06550: line 6, column 1:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Record type's name is not its initializer.
In fact, there is no initialization-like function for record types at all in oracle:
Documentation
Note that values are assigned separately to each field of a record in
Example 5-47. You cannot assign a list of values to a record using an
assignment statement. There is no constructor-like notation for
records.
You have to create each object separately and then create collection with them:
declare
type tab_name is record (t_name varchar2(30),col_name varchar2(30));
type tab_list is table of tab_name;
name_1 tab_name;
table_names tab_list;
begin
name_1.t_name := 'ABC';
name_1.col_name := 'abc';
table_names := tab_list();
table_names.extend;
table_names(table_names.last) := name_1;
name_1.t_name := 'XYZ';
name_1.col_name := 'xyz';
table_names.extend;
table_names(table_names.last) := name_1;
for i in table_names.first..table_names.last
loop
dbms_output.put_line(table_names(i).t_name||'-'||table_names(i).col_name);
end loop;
end;
Collections DO have initializers. However, in your case you have a collection of record types, so you would have to have your records pre-created to make use of it.

PLSQL script not working with passing multiple values into IN() caluse

I am very new in PLSQL, i want to pass an array of number into IN() clause value, But Sqldeveloper throw following error messages:
Error report -
ORA-06550: line 11, column 60:
PLS-00382: expression is of wrong type
ORA-06550: line 11, column 53:
PL/SQL: ORA-22905: cannot access rows from a non-nested table item
ORA-06550: line 10, column 4:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
This is my code :
DECLARE
TYPE sc IS TABLE OF transactionhistory.NBSUBCOMPANY%TYPE INDEX BY PLS_INTEGER;
arr sc;
BEGIN
arr(0) := 000;
arr(1) := 111;
arr(2) := 222;
arr(3) := 333;
select count(th.CHCARDNUMBER) as transactions from transactionhistory th INNER JOIN cards ch on ch.NBATTMID=th.NBATTMID where th.dtdate>=to_date('01-oct-2016','dd-mon-yyyy') and th.dtdate<to_date('01-nov-2016','dd-mon-yyyy')
and ch.NBSUBCOMPANY IN (select column_value from table (arr))
and ((th.CHTRANSTYPE in ('2940', '2916', '2941', '2942', '2943', '2944', '2945', '2902', '2917', '2925') and th.NBBASEAMT < 0) or (th.CHTRANSTYPE in ('2922', '2923', '2926', '2950', '2951', '2952', '2953', '2954', '2955') and th.NBBASEAMT > 0) or (th.CHTRANSTYPE in ('1101', '1102', '1104', '1105', '1106', '1107', '1109') and th.BASEII_STATUS = 'C') or th.CHTRANSTYPE not in ('2940', '2916', '2941', '2942', '2943', '2944', '2945', '2902', '2917', '2925', '2922', '2923', '2926', '2950', '2951', '2952', '2953', '2954', '2955', '1101', '1102', '1104', '1105', '1106', '1107', '1109'));
END;
Please suggest me how can i pass this.
You will need to have a workaround.
SQL doesn't support local collection types to be used in the way you want.
Sample Procedure below which will help in resolving
CREATE OR REPLACE type TEST_TYPE
IS
TABLE OF NUMBER;
You will need to create a type in the database for this to work. Can be created inside a package, if you have one.
DECLARE
TEST_VAR TEST_TYPE := TEST_TYPE();
BEGIN
TEST_VAR.extend(1);
TEST_VAR(TEST_VAR.last) := 222;
TEST_VAR.extend(1);
TEST_VAR(TEST_VAR.last) := 333;
FOR rec IN
(SELECT column_value
FROM TABLE(TEST_VAR)
)
LOOP
dbms_output.put_line(rec.column_value);
END LOOP;
END;
Output
222
333
This way, you can use select column_value from table(test_var) for your IN() clause.
Also, you don't necessarily need to follow the extend(i) part. you can simply do the below as well
TEST_VAR TEST_TYPE := TEST_TYPE(222,333);
Have a read - local collection types not allowed in SQL statements
You can use MEMBER OF clause. See below:
As #Sudipta mentioned you cannot use a collection decalred in PLSQL block as you are doing, you need to declare it outside of the PLSQL block.
CREATE OR REPLACE TYPE sc IS TABLE OF NUMBER;
and then
DECLARE
-- TYPE sc IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
arr sc:=sc();
num number;
BEGIN
arr.extend(4);
arr(1) := 0;
arr(2) := 1;
arr(3) := 2;
arr(4) := 3;
Select count(1)
into num
from employee
-- You can use either commented one or uncommented one. your choice.
--where employee_id in (select column_value from table(arr));
where employee_id member of arr;
END;

Simple PL/SQL function to test if a string is a number

I'm experienced with T-SQL from SQL Server, but have recently begun working on a project utilizing an Oracle database (11g) and am having some issues writing what seems to be basic code.
I need to test whether a set of values are numeric, and only insert them into a table if they are. PL/SQL doesn't seem to have an is_number function, so I wrote my own based on an AskTom question.
create or replace
function IS_NUMBER(str in varchar2) return boolean
IS
n number;
BEGIN
select to_number(str) into n from dual;
return (true);
EXCEPTION WHEN OTHERS THEN
return (false);
END;
Eventually, I'd like to use this function in a WHERE clause, but for now I'm just trying to get it to run at all:
declare
str varchar2(1);
n boolean;
begin
str := '0';
select ca_stage.is_number(str) into n from dual;
end;
In SQL Developer, trying to run this gives me the following error report:
Error report:
ORA-06550: line 6, column 39:
PLS-00382: expression is of wrong type
ORA-06550: line 6, column 19:
PLS-00382: expression is of wrong type
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
The error report is straight-forward, but doesn't make sense. The function accepts a varchar2, and that's what I'm using as an input variable. It returns a boolean and again, that's what I'm using.
As I said, this is basic code, so I assume that I'm missing something fundamental.
Return a SQL datatype, e.g. VARCHAR2. Also, I'd recommend against using WHEN OTHERS. Also, you don't need a query on dual:
create or replace
function IS_NUMBER(str in varchar2) return varchar2
IS
n number;
BEGIN
n := to_number(str);
return 'Y';
EXCEPTION WHEN VALUE_ERROR THEN
return 'N';
END;

retrieve recordset with more than one column value using refcursor in oracle

am creating a package in pl/sql . with in this i declared the ref cursor . With in procedure am using select statement with multiple column name . but am not able to get the result.
here i attached my code. Help me to correct the error. Am new to pl/sql
code
CREATE OR REPLACE PACKAGE types AS
TYPE cursor_type IS REF CURSOR;
END Types;
/
CREATE OR REPLACE
PROCEDURE get_CDR_rs (p_no IN zkv.FLD_callingPartyNumber%TYPE,
CDR_recordset OUT SYS_REFCURSOR) AS
BEGIN
OPEN CDR_recordset FOR
SELECT FLD_callingPartyNumber,
FLD_dateTimeConnect
FROM CISCOCUIC_TBL
WHERE FLD_callingPartyNumber= p_no
ORDER BY FLD_callingPartyNumber,;
END get_CDR_rs;
/
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
l_cursor SYS_REFCURSOR;
l_callingPartyNumber zkv.FLD_callingPartyNumber%TYPE;
l_dateTimeConnect zkv.FLD_dateTimeConnect%TYPE;
BEGIN
LOOP
FETCH l_cursor
INTO l_callingPartyNumber, l_dateTimeConnect;
EXIT WHEN l_cursor%NOTFOUND;
END LOOP;
CLOSE l_cursor;
END;
/
Error
9/41 PL/SQL: ORA-00936: missing expression
5/5 PL/SQL: SQL Statement ignored
First thing is there is a syntax error in the procedure. It should be
CREATE OR REPLACE
PROCEDURE get_CDR_rs (p_no IN zkv.FLD_callingPartyNumber%TYPE,
CDR_recordset OUT SYS_REFCURSOR) AS
BEGIN
OPEN CDR_recordset FOR
SELECT FLD_callingPartyNumber,
FLD_dateTimeConnect
FROM CISCOCUIC_TBL
WHERE FLD_callingPartyNumber= p_no
ORDER BY FLD_callingPartyNumber; -- there was a comma which is not required or you
-- missed a column
END get_CDR_rs;
/
Secondly where is get_CDR_rs being called to retrieve the results?
Thirdly why do you need the following? because you are using sys_refcursor
CREATE OR REPLACE PACKAGE types AS
TYPE cursor_type IS REF CURSOR;
END Types;
/
If you would like to see the results of your procedure which returns sys_refcursor, do as follows
variable rset refcursor;
DECLARE
p_no zkv.FLD_callingPartyNumber%TYPE;
BEGIN
p_no := '123';
get_CDR_rs (p_no, :rset);
END;
/
print rset

Dynamic column name to record type variable

DECLARE
TYPE t IS RECORD (
col_name VARCHAR2 (100)
);
t_row t;
cname VARCHAR (100) := 'col_name';
BEGIN
t_row.col_name := 'col';
DBMS_OUTPUT.put_line ('out');
IF t_row.cname IS NULL THEN
DBMS_OUTPUT.put_line ('in');
END IF;
END;
Error at line 1
ORA-06550: line 12, column 12:
PLS-00302: component 'CNAME' must be declared
ORA-06550: line 12, column 3:
PL/SQL: Statement ignored
How can I assign dynamic column name to type variable of record?
You can do that with dynamic sql:
To make the example simpler I'll make your type t a schema object (but basically you don't have to - you can put it in the dynamic part as well)
create or replace type t is object(col_name varchar2(100));
/
Then you can look at this script:
declare
t_row t;
cname varchar2(100) := 'col_name';
begin
t_row := new t('col');
execute immediate 'declare t_in t := :0; begin if t_in.' || cname ||
' is null then dbms_output.put_line(''in''); end if; end;'
using t_row;
end;
Though, I must say, that this is a strange requirement ...
The error is because record t doesn't have a field cname, but col_name:
type t is record (
col_name varchar2(100)
);
One have to know record fields during compile time.
Could tell us what is the real problem you're going to solve ?

Resources