Error On Oracle Function That Recursively Selects child elements of parent - sql

I am trying to write an oracle function that takes in a parentID and returns me a "table" that recursively finds its child and child's child and so on.. Here is my code for it but whenever I try and create the function I get these errors :
[Error] Compilation (17: 10): ORA-00604 : error occurred at recursive SQL level 1
ORA-01422 : exact fetch returns more than requested number of rows
ORA-06512
ORA-06512
And here is code:
CREATE OR REPLACE TYPE TAB_TableOfIDs IS TABLE OF NUMBER;
CREATE OR REPLACE FUNCTION RETURNCHILDIDS (pParentID NUMBER)
RETURN TAB_TableOfIDs
AS
ResultSet TAB_TableOfIDs;
BEGIN
WITH MY_VIEW (ID)
AS (SELECT A.ID
FROM MYTABLE A
WHERE A.ID = pParentID
UNION ALL
SELECT E.ID
FROM MYTABLE E
INNER JOIN MY_VIEW B
ON E.ParentID = B.ID)
select bulk collect into ResultSet from MY_VIEW;
RETURN ResultSet;
END;
/
The interesting part is , the code just works fine in a normal SQL query editor but if you put it in a function it gives the error.. So what am I doing wrong? and how can I fix it?
Thank you in advance.

I had my solution :
CREATE OR REPLACE FUNCTION RETURNCHILDIDS (pParentID NUMBER)
RETURN TAB_TableOfIDs
IS
CURSOR c1
IS
WITH ALT_BIRIM (targetID, ID, parentID)
AS (SELECT A.targetID, A.ID, A.PARENTID
FROM ENTKURUM A
WHERE A.ID = (SELECT ID
FROM ENTKURUM
WHERE targetID = pParentID)
UNION ALL
SELECT E.targetID, E.ID, E.PARENTID
FROM ENTKURUM E
INNER JOIN ALT_BIRIM B
ON E.PARENTID = B.ID)
SELECT targetID
FROM alt_birim;
ResultSet TAB_TableOfIDs := NEW TAB_TableOfIDs ();
BEGIN
FOR mrow IN c1
LOOP
ResultSet.EXTEND ();
ResultSet (ResultSet.COUNT) := mrow.targetID;
END LOOP;
RETURN ResultSet;
END;

Either:
Rewrite your SELECT INTO statement so that only one row is returned.
OR
Create a cursor and retrieve each row if you are unsure of how many records you might retrieve.
CREATE OR REPLACE FUNCTION RETURNCHILDIDS (pParentID IN NUMBER)
RETURN number
IS
cnumber number;
CURSOR c1
IS
[Your query]
...
BEGIN
OPEN c1;
FETCH c1 INTO cnumber;
CLOSE c1;
RETURN cnumber;
END;

Related

run a query saved in a row value

The following select statement gives me three cols and one row.
QueryA
SELECT
-- some calculations and manipulations
x, y, queryB
.....
FROM INFORMATION_SCHEMA.COLUMNS c
WHERE TABLE_SCHEMA = UPPER($sch_name)
AND TABLE_NAME = UPPER($tab_name);
One of the returned columns has a query stored. Output
x, y, queryB
aa bb select * from stg.new
How can I extend the QueryA such that I select and run the query B and the final output returned is that of queryB not queryA?
Here is a sample of reading from one table to get the SQL, executing the SQL on a different table, and then returning the result of that second statement:
create or replace transient table T1 as
select 'This is the final result.' as THE_RESULT
;
create or replace transient table SQL_TO_RUN as
select 'select * from T1' SQL_STATEMENT
;
create or replace procedure myprocedure()
returns table()
language sql
execute as caller
as
$$
declare
rs resultset default (select SQL_STATEMENT from SQL_TO_RUN);
c cursor for rs;
sqlStatement string;
rsFinal resultset;
begin
for rowContents in c do
sqlStatement := rowContents.SQL_STATEMENT;
end for;
rsFinal := (execute immediate :sqlStatement);
return table(rsFinal);
end;
$$
;
call myprocedure();

Bulk Collect with Sum function

I am trying to use Bulk all and Forall in Oracle database:
Original code from Procedure is as below:
IF NVL(v_mc,0) != 0 THEN
FOR rec IN
(SELECT a.testid,
SUM(pct * NVL(cap,0))/v_mc lead1
BULK COLLECT INTO testids1, testids2
FROM testtable a
WHERE a.id = n_id
AND a.type =n_type
GROUP BY a.testid;
)
LOOP
UPDATE testtable
SET LEAD1 =ROUND(testids2(i),2)
WHERE tid = n_id
AND type = n_type
AND testid =testids1(i);
END LOOP;
END IF;
So In select statement , I am using Sum function and also using aliasing here .
Code , I have written which use Bulk collect and Forall is as follows:
PROCEDURE test
IS
TYPE test1Tab IS TABLE OF sh_rpt_temp_peer_wip.test1%TYPE;
TYPE test2Tab IS TABLE OF testtable.lead1%TYPE;
testids1 testidTab; --Error 1 and Error 2
testids2 LeadTab;
BEGIN
IF NVL(v_mc,0) != 0 THEN
SELECT testid,
SUM(pct * NVL(cap,0))/v_mc lead1
BULK COLLECT INTO testids1, testids2
FROM testtable a --Error 3
WHERE a.id = n_id
AND a.type =n_type
GROUP BY a.testid ORDER BY a.testid;
FORALL i IN testids1.FIRST..testids1.LAST
UPDATE testtable
SET LEAD1 =ROUND(testids2(i),2)
WHERE tid = n_id --Error 3
AND type = n_type
AND testid =testids1(i);
END IF;
END;
But while I am compiling procedure , I am getting multiple errors. I am very new to PL/SQL. Please let me know if I can retrieve calculated value as a Column in Bulk Collect?
I am getting below errors in procedure:
Error 1) PL/SQL: Item ignored
Error 2) component 'LEAD' must be declared
Error 3) expression is of wrong type
Please let me know what is wrong here
Thanks
As I identified that the collection type that you are referring is not in scope in the procedure, might be you have declared globally. I modified your code get a try it once, hopefully, it works for you.
PROCEDURE test
IS
TYPE test1Tab IS TABLE OF testtable.testid%TYPE;
TYPE test2Tab IS TABLE OF number;
testids1 test1Tab; //Error 1 and Error 2
testids2 test2Tab;
BEGIN
IF NVL(v_mc,0) != 0 THEN
SELECT testid,
SUM(pct * NVL(cap,0))/v_mc lead
BULK COLLECT INTO testids1, testids2
FROM testtable a //Error 3
WHERE a.id = n_id
AND a.type =n_type
GROUP BY a.testid ORDER BY a.testid;
FORALL i IN testids1.FIRST..testids1.LAST
UPDATE testtable
SET LEAD = ROUND(testids2(i),2)
WHERE tid = n_id //Error 3
AND type = n_type
AND testid = testids1(i);
END IF;
END;

dbms_xmlgen.getxmltype(context) returns null when the sql table is empty

I have this following function to convert table data to XML. However, whenever the table data is null the resulting XML is null. I want it to add the column names in case of empty table data.
What changes should I make in this function.
function XmlToBlob(p_query varchar2) return blob is
ctx dbms_xmlgen.ctxhandle;
xml xmltype;
resultBlob blob;
begin
ctx := dbms_xmlgen.newcontext(p_query);
dbms_xmlgen.setNullHandling(ctx, dbms_xmlgen.empty_tag);
xml := dbms_xmlgen.getxmltype(ctx);
if (xml is not null) then
resultBlob := xml.getBlobVal(NLS_CHARSET_ID('AL32UTF8'));
else
resultBlob := null;
end if;
return resultBlob;
end;
You should change your query so that it will return a single row of null values if no other data can be retrieved.
Try this:
FOR ORACLE:
select emp.*
from dual
left join emp on ( 1 = 1 )
FOR MSSQL SERVER:
select t.*
from (select 1 as adummy) a
left join (select * from emp ) t on 1=1

Getting an error in sql, when executing code below.How to declare a table type in plsql. Am a beginner . Please suggest

create or replace procedure BAS_NUM_UPD is
cursor cur is
select distinct o.oi_b,mpr.pa_ke_i,ltrim(substr(convert_171_to_711(cp.p_t_num),1,7),'0') bs_nbr
from t_obj o, mat_pa_rel mp, cor_pa cp
where o.ob_t = 'something'
and o.oi_b = mp.oi_b
and mp.pa_ke_i = cp.pa_ke_i;
l_ba_num_at_i number(10) := get_attribute_id('Ba timber');
flag1 VARCHAR2(10);
type t1 is table of varchar2(10);
par_k t1;
BEGIN
for x in cur loop
BEGIN
select pa_ke_i into par_k from mat_pa_rel where oi_b=x.oi_b ;
if par_k.count=null then
insert into cs_val (oi_b, at_i, value, flag, ) values (x.oi_b, l_ba_num_at_i, null, 1);
end if;
select flag into flag1 from cs_val where at_i = l_ba_num_at_i and oi_b = x.oi_b
and value = x.bs_nbr;
EXCEPTION
when NO_DATA_FOUND THEN
insert into cs_val (oi_b, at_i, value, flag, )
values (x.oi_b, l_ba_num_at_i, x.bs_nbr, 1);
flag1 :='Nothing';
when OTHERS
then
raise_application_error(-20011,'Unknown Exception in PROCEDURE');
END;
end loop;
end BAS_NUM_UPD;
error:
PLS-00642: local collection types not allowed in SQL statements
You should get it running if you do a bulk collect
select pa_ke_i bulk collect into par_k from mat_pa_rel where oi_b=x.oi_b ;
Then I think the if is not right. I think you need to do
if par_k.count = 0 then
But to be honest you might just make a count
select count(*) into l_cnt from mat_pa_rel where oi_b=x.oi_b;
If l_cnt = 0 then ...
Of course l_cnt has to be defined.
You should create type t1 in the schema and not in the pl/sql block.

Oracle: Return multiple values in a function

I'm trying to return a multiple values in a %rowtype from a function using two table(employees and departments), but it not working for me.
create or replace function get_employee
(loc in number)
return mv_emp%rowtype
as
emp_record mv_emp%rowtype;
begin
select a.first_name, a.last_name, b.department_name into emp_record
from employees a, departments b
where a.department_id=b.department_id and location_id=loc;
return(emp_record);
end;
The above function compiled without any error? What is the type of MV_EMP? Ideally, it should be something like below.
create or replace type emp_type
(
first_name varchar2(20)
, last_name varchar2(20)
, depart_name varchar2(20)
)
/
create or replace function get_employee
(loc in number)
return emp_type
as
emp_record emp_type;
begin
select a.first_name, a.last_name, b.department_name into emp_record
from employees a, departments b
where a.department_id=b.department_id and location_id=loc;
return(emp_record);
end;
create type t_row as object (a varchar2(10));
create type t_row_tab as table of t_row;
We will now create a function which will split the input string.
create or replace function get_number(pv_no_list in varchar2) return t_row_tab is
lv_no_list t_row_tab := t_row_tab();
begin
for i in (SELECT distinct REGEXP_SUBSTR(pv_no_list, '[^,]+', 1, LEVEL) no_list FROM dual
CONNECT BY REGEXP_SUBSTR(pv_no_list, '[^,]+', 1, LEVEL) IS NOT NULL)
loop
lv_no_list.extend;
lv_no_list(lv_no_list.last) := t_row(i.no_list);
end loop;
return lv_no_list;
end get_number;
Once the function is in place we can use the table clause of sql statement to get the desired result. As desired we got multiple values returned from the function.
SQL> select * from table(get_number('1,2,3,4'));
A
----------
1
3
2
4
So now our function is simply behaving like a table. There can be a situation where you want these comma separated values to be a part of "IN" clause.
For example :
select * from dummy_table where dummy_column in ('1,2,3,4');
But the above query will not work as '1,2,3,4' is a string and not individual numbers. To solve this problem you can simply use following query.
select * from dummy_table where dummy_column in ( select * from table(get_number('1,2,3,4')) );
References : http://www.oraclebin.com/2012/12/returning-multiple-values-from-function.html
CREATE OR replace FUNCTION Funmultiple(deptno_in IN NUMBER)
RETURN NUMBER AS v_refcursur SYS_REFCURSOR;
BEGIN
OPEN v_refcursor FOR
SELECT *
FROM emp
WHERE deptno = deptno_in;
retun v_refcursor;
END;
To call it, use:
variable x number
exec :x := FunMultiple(10);
print x