Say I have this Object type:
create or replace
TYPE "InvestorInfoObject" AS OBJECT
(
"InvestorAccount" Varchar2(15 Char),
"InvestorSeqNo" NUMBER(15,0),
"FirstName" VARCHAR2(50 CHAR) ,
);
I need a function to select some values from different tables and return an "InvestorInfoObject". something like this:
create or replace
FUNCTION "GetInvestorInfo"
(
Par_InvestorAccount "Pos"."InvestorAccount"%Type
)
Return "InvestorInfoObject" As
investorObj "InvestorInfoObject";
Begin
Select
"InvestorInfoObject"(InvAccounts."InvestorAccount",InvAccounts."InvestorSeqNo",Individuals."FirstName")
into investorObj
From "InvestorAccounts" InvAccounts
Inner Join "Individuals" Individuals
On InvAccounts."InvestorSeqNo"=Individuals."Seq"
Where "InvestorAccount"=Par_Investoraccount;
RETURN Investorobj;
END "GetInvestorInfo";
but this results in Error "Error(17,5): PL/SQL: SQL Statement ignored". what is the correct syntax to do this?
if you are asking about returning more then one value that can be done using collection this
can be used
declare
type student_mark is varray(10) of integer;
x student_mark;
function double_marks(x student_mark) return
student_mark is
y student_mark;
begin
y := x;
for i in 1..x.count loop
y(i) := 2*y(i);
end loop;
return y;
end;
begin
x := student_mark(156, 206, 360);
dbms_output.put_line(double_marks(x)(1));
dbms_output.put_line(double_marks(x)(2));
dbms_output.put_line(double_marks(x)(3));
end;
here you can use object also instead of varray . i will get back to you once i execute the
code provided by you
Related
I am trying to do build a sentence using a UDT, as follows:
CREATE OR REPLACE TYPE fv_group as object(
fv NUMBER,
group_number INTEGER
);
/
CREATE OR REPLACE TYPE fv_group_array IS VARRAY(100) OF fv_group;
CREATE OR REPLACE TYPE fv_grouping AS OBJECT (
fv_and_group fv_group_array,
MEMBER PROCEDURE insert_groupby(FV NUMBER),
MEMBER FUNCTION which_group(FV NUMBER) RETURN INTEGER
);
/
CREATE OR REPLACE TYPE BODY fv_grouping as
MEMBER PROCEDURE insert_groupby(FV NUMBER) IS
g fv_group;
BEGIN
IF fv < 15 THEN
g := fv_group(fv,1);
ELSE
g := fv_group(fv,2);
END IF;
fv_and_group.extend(1);
fv_and_group(fv_and_group.last) := g;
END;
MEMBER FUNCTION which_group(FV NUMBER) RETURN INTEGER IS
feature NUMBER;
BEGIN
FOR i IN 1..fv_and_group.count LOOP
feature := fv_and_group(i).fv;
IF fv_and_group(i).fv = fv THEN
RETURN fv_and_group(i).group_number;
END IF;
END LOOP;
RETURN 0;
END;
END;
/
The representation I need is:
DECLARE
obj fv_grouping;
BEGIN
SELECT :obj.which_group(gb.fv), count(*)
FROM (
SELECT :obj.insert_groupby(c.fv, 6, 3)
from cophir
) gb
GROUP BY :obj.which_group(gb.fv);
END;
/
The procedure insert_groupby inserts each value of the cophir table into a varray, which holds its values and the corresponding group.
After the varray is loaded with all values and their corresponding group, I want to group them. Is it possible, to do it in a query?
Thanks in advance!
grouping is a type. What is wrong?
GROUPING is an Oracle keyword. If you re-name your type to say FV_GROUPING you will solve the PLS-00488. Which will leave you free to address all the other syntax errors in your code:
The object type is not instantiated correctly in the query.
SELECT statements embedded in PL/SQL must select into a variable which matches the projection of the query.
Not sure what your code is trying to achieve, but this version of your code runs:
DECLARE
obj fv_grouping;
BEGIN
SELECT fv_grouping(cast(collect(fv_group(fv, 3)) as fv_group_array))
into obj
from cophir;
dbms_output.put_line(obj.which_group(2)) ;
END;
/
This uses COLLECT to gather the fv_group objects into an fv_group_array instance which can be used to instantiate fv_grouping and populate the obj variable.
I have a PL/SQL stored proc which needs to return a RefCursor type object as output parameter.
PROCEDURE usp_appnt_stts_driver_wraper2
( in_req_src_system_id IN NUMBER,
in_req_user_info IN VARCHAR2,
out_response_rec1 OUT SYS_REFCURSOR)
At the end of the SP, I am able to return Hard Coded values to my front end by using a Select Statement.
OPEN out_response_rec1 FOR
SELECT 'data1', 'data2', 'data3', 'data 4' FROM DUAL;
This works fine. But I need to send the data which I am getting from an Array.
The Array is populated like this,
FOR g_index IN g_slsnetoutbndarr.FIRST..g_slsnetoutbndarr.LAST
LOOP
out_response_rec.EXTEND;
IF g_SlsnetOutbndArr(g_index).Rectypdesc IS NOT NULL THEN
out_response_rec(g_index).Rectypdesc := g_SlsnetOutbndArr(g_index).Rectypdesc ;
out_response_rec(g_index).Recdetltcode := g_SlsnetOutbndArr(g_index).Recdetltcode;
out_response_rec(g_index).RecDetlDesc := g_SlsnetOutbndArr(g_index).RecDetlDesc ;
END IF;
END LOOP;
So at the end of this code, the Array Object out_response_rec has all the values I need.
But How do I transfer these values in the RefCursor output parameter?
Update 1
I have tried to create a new data type in the Package specification.
TYPE SlsnetOutbndRec IS RECORD(
Rectypdesc VARCHAR2(30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2(130));
TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec;
Finally I have tried to Cast the Array to table in my SP as
OPEN out_response_rec_result FOR
SELECT * FROM TABLE (Cast(out_response_rec AS SlsnetOutbndTabArr));
But this is throwing an Invalid Data Type error. The SP does not recognize the new data types I created.
So at the end of this code, the Array Object out_response_rec has all
the values I need.
But How do I transfer these values in the RefCursor output parameter?
As I could understand, you wanted to use a SYS_REFCUSOR to get the output of a Query plus the values that are in collection which is being populated by another Procedure. I have come up with a solution for your requirement see below inline explaination:
--Create a Object in Place of Record since we cannot use a PLSQL scope compnenet in SQL scope in Oracle 11g and below
CREATE OR REPLACE TYPE SlsnetOutbndRec IS OBJECT
(
Rectypdesc VARCHAR2 (30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2 (130)
);
--Create a table type of the Object
CREATE OR REPLACE TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec;
/
--Procedure
CREATE OR REPLACE PROCEDURE combined_rslt (var OUT SYS_REFCURSOR)
AS
v_var SlsnetOutbndTabArr := SlsnetOutbndTabArr ();
l_var SlsnetOutbndTabArr := SlsnetOutbndTabArr ();
BEGIN
--Populating the collection
v_var.EXTEND;
v_var (1) := SlsnetOutbndRec ('ABC', 1, 'A');
OPEN VAR FOR
SELECT 'CDE', 2, 'B' FROM DUAL
UNION ALL -- Combining the result of collection with the result of query
SELECT *
FROM TABLE (v_var) t;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
END;
Execution :
DECLARE
x SYS_REFCURSOR;
a VARCHAR2 (30);
b NUMBER;
c VARCHAR2 (130);
BEGIN
combined_rslt (var => x);
LOOP
FETCH x INTO a, b, c;
EXIT WHEN x%NOTFOUND;
DBMS_OUTPUT.put_line (a || ' ' || b || ' ' || c);
END LOOP;
END;
Results:
SQL> /
CDE 2 B
ABC 1 A
PL/SQL procedure successfully completed.
You need to create the types in the SQL scope (not the PL/SQL scope) if you want to use them in SQL statements (in versions prior to Oracle 12):
CREATE TYPE SlsnetOutbndRec IS OBJECT(
Rectypdesc VARCHAR2(30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2(130)
)
/
CREATE TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec
/
Then you can use it in your procedure (assuming your 3rd party data type is a collection or a VARRAY):
PROCEDURE usp_appnt_stts_driver_wraper2(
in_req_src_system_id IN NUMBER,
in_req_user_info IN VARCHAR2,
out_response_rec_result OUT SYS_REFCURSOR
)
IS
out_response_rec SlsnetOutbndTabArr := SlsnetOutbndTabArr();
g_slsnetoutbndarr ThirdPartyDataType := Get_From_3rd_party_Package();
BEGIN
FOR i IN 1 .. g_slsnetoutbndarr.COUNT LOOP
IF g_SlsnetOutbndArr(i).Rectypdesc IS NOT NULL THEN
out_response_rec.EXTEND;
out_response_rec := SlsnetOutbndRec(
g_SlsnetOutbndArr(i).Rectypdesc,
g_SlsnetOutbndArr(i).Recdetltcode,
g_SlsnetOutbndArr(i).RecDetlDesc
);
END IF;
END LOOP;
OPEN out_response_rec_result FOR
SELECT *
FROM TABLE( out_response_rec );
END;
If the 3rd party data type is an associative array then:
...
IS
out_response_rec SlsnetOutbndTabArr := SlsnetOutbndTabArr();
g_slsnetoutbndarr ThirdPartyDataType := Get_From_3rd_party_Package();
i ThirdPartyIndexType := g_slsnetoutbndarr.FIRST;
BEGIN
WHILE i IS NOT NULL LOOP
IF g_SlsnetOutbndArr(i).Rectypdesc IS NOT NULL THEN
out_response_rec.EXTEND;
out_response_rec := SlsnetOutbndRec(
g_SlsnetOutbndArr(i).Rectypdesc,
g_SlsnetOutbndArr(i).Recdetltcode,
g_SlsnetOutbndArr(i).RecDetlDesc
);
END IF;
i := g_slsnetoutbndarr.NEXT(i);
END LOOP;
...
I need to pass a locally defined table type to a function which is a pipelined function returning another locally defined table type.
Here is sample data:
create table my_tab
(i NUMBER,
n VARCHAR2(30));
insert into my_tab values (1, 'Peter');
insert into my_tab values (2, 'Dakshesh');
insert into my_tab values (1, 'Maggie');
insert into my_tab values (3, 'Madhu');
commit;
My code is:
CREATE OR REPLACE PACKAGE my_pkg IS
TYPE t_col IS RECORD(
i NUMBER,
n VARCHAR2(30));
TYPE t_nested_table IS TABLE OF t_col;
TYPE t_number IS TABLE OF NUMBER;
FUNCTION iterate_table RETURN t_number PIPELINED;
FUNCTION return_table(in_t_num t_number) RETURN t_nested_table PIPELINED;
g_number t_number ;
g_nested_number t_nested_table ;
END my_pkg;
/
Body:
CREATE OR REPLACE PACKAGE BODY my_pkg IS
FUNCTION iterate_table RETURN t_number PIPELINED IS
BEGIN
IF ( (g_number IS NOT NULL) AND (g_number.EXISTS (1)))
THEN
FOR i IN 1 .. g_number.COUNT
LOOP
IF g_number (i) IS NOT NULL
THEN
PIPE ROW (g_number (i));
END IF;
END LOOP;
END IF;
RETURN;
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END iterate_table;
FUNCTION return_table(in_t_num t_number) RETURN t_nested_table PIPELINED IS
l_row t_nested_table ;
CURSOR cur_test IS
select mt.i, mt.n
from my_tab mt, TABLE(iterate_table ) tab
where mt.i = tab.column_value;
BEGIN
OPEN cur_test;
FETCH cur_test BULK COLLECT into l_row;
CLOSE cur_test;
FOR i IN 1..l_row.COUNT
LOOP
PIPE ROW(l_row(i));
END LOOP;
RETURN;
END return_table;
END my_pkg;
/
Now this code compiles successfully, when I try to invoke it like a pipelined function, it gives error-
select * from table(my_pkg.return_table(my_pkg.t_number(1)));
Error-
ORA-00902: invalid datatype
00902. 00000 - "invalid datatype"
*Cause:
*Action:
Error at Line: 14 Column: 41
The two pre-requisites for this code are -
the collections should be all locally defined.
the function should be pipelined.
Help!!
I am not getting any error with table and its a single table so no foreign key constraints hold.
It is not possible in oracle 11. I don't know why oracle throws "invalid data type".
If you put this query into anonymous block you will receive PLS-00642: Local Collection Types Not Allowed in SQL Statement
If you don't want or you cannot create sql level collection. The solution is use predefined type. Good source of predefined collection is Oracle Data Cartridge. Replace all t_number with ODCINumberList.
predefined collections
In Oracle 10 and 11, it is not possible to use collections in the SQL scope that have been defined in a PL/SQL scope. In Oracle 12, what you are trying should work.
If you want to do this then declare your types in the SQL scope (not in the package).
CREATE OR REPLACE TYPE t_col IS OBJECT(
i NUMBER,
n VARCHAR2(30)
);
/
CREATE OR REPLACE TYPE t_nested_table IS TABLE OF t_col;
/
CREATE OR REPLACE TYPE t_number IS TABLE OF NUMBER;
/
CREATE OR REPLACE PACKAGE my_pkg IS
FUNCTION iterate_table RETURN t_number PIPELINED;
FUNCTION return_table(in_t_num t_number) RETURN t_nested_table PIPELINED;
g_number t_number ;
g_nested_number t_nested_table ;
END my_pkg;
/
I have a DB table in which one column have an array of values.
Lets assume
Student table
"Name" character(10),
"Subject_studying" text[],
I created a stored procedure as follows
CREATE OR REPLACE FUNCTION ArrayLike(vals text[], v text) RETURNS integer AS $$
DECLARE
str text;
BEGIN
v := replace(replace(v, '%', '.*'), '_', '.');
FOREACH str IN ARRAY vals LOOP
IF str ~* v THEN
RETURN 1;
END IF;
END LOOP;
RETURN 0;
END; $$
LANGUAGE PLPGSQL;
Which returns all the subjects that I'm looking for.
How do I pass the parameter of the subjects to the stored procedure.
My query looks like
SELECT Name FROM Student WHERE ArrayLike('Subject_studying', 'english') = 1
The query is giving me an error
ERROR: FOREACH expression must not be null
CONTEXT: PL/pgSQL function Arraylike(text[],text) line 6 at FOREACH over array
********** Error **********
I guess the parameter 'Subject_studying' is not sent as a value but as a simple string. How do we pass the values in that field to the stored procedure?
The error is caused by passing the column name subject_studying as a string, as you already noted. Unquote it to make the error go away.
But you actually do not have to create your own function for this. You can use the built-in ANY operator:
SELECT Name FROM Student WHERE 'english' = ANY(subject_studying);
I would like to extract a function from a piece of SQL-code which is used multiple times in one query. I'm looking for a functionality which is similar to the following (invented by me) syntax:
with f(x) as (return x+1)
select f(thing1), f(thing2), f(thing3) from things
thing1, thing2, thing3 are integer columns in the table "things" in the example. Also, imagine that f is more complicated than an add-one function.
How do I define a function inside a query?
Declaration of a function in the WITH clause of a query is not possible but according to the information presented at OOW it will be in 12c version. So for now you need to create a function as a schema object whether it would be a stand-alone function or part of a package. For example:
create or replace function F(p_p in number)
return number
is
begin
return p_p + 1;
end;
And then call it in a query, ensuring that the data type of a column you are passing in to the function as a parameter is of the same data type as the parameter of the function:
select f(col1)
, f(col2)
, ...
, f(coln)
from your_table
Are you trying to build a function for dynamic table and table's columns, I would do some code like this? And you cannot declare a function in the WITH clause of a query.
SELECT f ( tablename,columnname1 ),
f ( tablename,columnname2 ),
........
FROM tablename;
Create or replace function f (tableName varchar2,ColumnName varchar2)
Return somethingHere
Is
varTableName varchar2(200);
varColumnName varchar2(200);
varValue integer;
t_cid INTEGER;
t_command VARCHAR2(200);
Begin
--Get tableName
varTableName := tableName ;
--Get columnName
varColumnName := ColumnName ;
t_command := 'SELECT ' || varColumnName ||' FROM ' || varTableName;
--Here execute dynamic sql statement
DBMS_SQL.PARSE
DBMS_SQL.DEFINE_COLUMN
DBMS_SQL.EXECUTE
--fatch row values into varValue
DBMS_SQL.COLUMN_VALUE (..,..,varValue);
--then do your x+1 magic here
varValue := varValue+1
--then output your value.
End;