Is there any way to pass a result from table(xmlsequence(extract(xmltype(xml), xpath))) to a function as a parameter?
I have a query:
select
extractvalue(value(data), '/path/to/element')
from
table(xmlsequence(extract(xmltype(in_xml), in_path))) data;
What I would like to do is to isolate all XML operations into a couple of functions and have something like this:
select
get_value(data, '/path/to/element')
from
get_table(in_xml, in_path) data;
OR at least:
select
get_value(data, '/path/to/element')
from
table(xmlsequence(get_table(in_xml, in_path)) data;
There is no problem with moving extract(xmltype(in_xml), in_path) into the get_table function but I have a trouble with passing the result to the get_value.
Any thoughts?
table() is a table collection expression, and it doesn't really make sense to pass an expression (or indeed a table) as a parameter.
The closest I can think off is to pass the intermediate result from xmlsequence(), obtained from a function if you want, directly into get_value():
create or replace function get_xmlseq(in_xml varchar2, in_path varchar2)
return xmlsequencetype
as
l_xmlseq xmlsequencetype;
begin
select xmlsequence(extract(xmltype(in_xml), in_path))
into l_xmlseq
from dual;
return l_xmlseq;
end get_xmlseq;
/
create or replace function get_value (in_xmlseq xmlsequencetype, in_path varchar2)
return varchar2
as
l_value varchar2(32767);
begin
select extractvalue(value(data), in_path)
into l_value
from table(in_xmlseq) data;
return l_value;
end get_value;
/
And then call it as:
select get_value(get_xmlseq(:in_xml, :in_path), '/path/to/element') from dual;
Which gives the same result as your original query, at least for a very simple example.
But if you're doing that then you might as well have a single function that takes (in_xml, in_path, '/path/to/element') and does the xmlsequence call as well. Or since that's deprecated, that uses xmltable and/or xquery.
Related
I have a pipelined function which returns results of a SQL query:
function my_pipelined_function(...)
return rec_t
PIPELINED is
begin
for l in (select * from ... join ... where ...) loop
PIPE ROW(l);
end loop;
return;
end;
This works, but I have to define the record and it's type in the package header:
TYPE rec IS RECORD(
some_date_param date,
some_number_param number,
...
);
TYPE rec_t IS TABLE OF rec;
Ugly. Isn't there a way to avoid having to declare the "hard-coded" record with all types defined, one by one?
I saw the ANYDATA type, but I can't make it work. Should I use this approach, if yes, how to do it? If not, should I use some kind of ref cursor, or is this simply not possible at all in Oracle?
Thanks
Why below function is not returning fresh param value every time I am altering session to set new NLS_DATE_FORMAT
FUNCTION get_param(p_parameter IN VARCHAR2)
RETURN VARCHAR2 RESULT_CACHE relies_on(nls_session_parameters) IS
l_value nls_session_parameters.value%TYPE;
BEGIN
dbg('Entered Fn_Get_nls_session_Parameter_frc to cache details for .. ' || p_parameter);
SELECT SYS_CONTEXT('USERENV', p_parameter) INTO l_value FROM dual;
RETURN l_value;
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbg('In NDF : Gng to return value as null.. ');
l_value := NULL;
RETURN l_value;
END get_param;
Well... I would say the answer stands in the question! If you carefully read Oracle documentation about Cross Session Functions, then you'd know.
The cross-session PL/SQL function result cache provides a simple way to boost the performance of PL/SQL functions by saving the results of function calls for specific combinations of input parameters in the SGA. These results can be reused by any session calling the same function with the same parameters.
This is exactly what you're using when creating your function:
FUNCTION get_param(p_parameter IN VARCHAR2)
RETURN VARCHAR2
RESULT_CACHE relies_on(nls_session_parameters)
IS
Indeed nls_session_parameters View doesn't change between your calls! it is a fixed system view. What changes it what your user sees from it.
So you have solutions:
simpler and inefficient(sorry): remove RESULT_CACHE statement from your function declaration or find a way to refresh the cache between the calls
add a parameter that will change between your calls:
FUNCTION get_param(p_parameter IN VARCHAR2, p_dummy_session_id IN NUMBER)
RETURN VARCHAR2 RESULT_CACHE relies_on(nls_session_parameters) IS
...
(you might need to actually do something with the "dummy" parameter for it to be taken into account)
1) With Oracle Database 11gR2, the RELIES ON clause is deprecated, which means that you don’t even have to list the dependencies: Oracle will figure everything out for you.
2) Moreover Oracle has V$RESULT_CACHE_OBJECTS. There are information about cached object.
3) You can also force the oracle to refresh 'result_cache'
declare
n number;
begin
n := DBMS_RESULT_CACHE.INVALIDATE (user,'GET_PARAM');
end;
i am new to oracle database. Trying to make a simple function but it is returning complete select query which is in single quotes. I do not know why. kindly help. and yes! it in the end of function compiling also says "hint: nm parameter never used" I am confused.
-- function to return a single letter grade
create or replace function update_grade(nm number) return varchar2
as
grd varchar2(3);
begin
grd := 'select gradeid from grade where nm between marks_s and markks_e';
return grd;
end;
You are not executing any query. You are just assigning the query string to the returned result variable.
I've not tried to compile it, but something like this:
create or replace function update_grade(nm number) return varchar2
as
grd varchar2(3);
begin
SELECT gradeid INTO grd from grade where nm between marks_s and markks_e;
return grd;
end;
You might consider adding handling for the case when no data is found:
EXCEPTION
WHEN NO_DATA_FOUND THEN
grd:= NULL;
I have some pipelined function:
create type my_tab_type as table of ...
create function my_func (X in number) return my_tab_type pipelined as
begin
loop
...
pipe row (...);
end loop;
return;
end;
Now I want to create another pipelined function my_func_zero which does the same as my_func but for fixed value of parameter: my_func_zero must be equivalent to my_func(0).
Can I implement my_func_zero without senseless and boring loop for processing every row returned by select * from table(my_func(0))?
P.S. That thread is a bit similar, but it does not contain answer to my question.
It's possible, but only if you don't declare your second function as pipelined because all functions of this type return results on row-by-row basis.
If you omit this requirement you can reach your target with bulk collect if you need typed cursor:
create function my_zero_func return my_tab_type
as
res_table my_tab_type;
begin
select my_type(field1, field2)
bulk collect into res_table
from table(my_func(0));
return res_table;
end;
Alternatively you can use untyped cursor:
create function my_ref_zero_func return sys_refcursor
as
vRes sys_refcursor;
begin
open vRes for select * from table(my_func(0));
return vRes;
end;
SQLFiddle
In a client application my_ref_zero_func results may be used without changes, but in the SQLFiddle it converted to XML representation because there are no way to demonstrate ref cursor with this tool.
I am following these steps, but I continue to get an error and don't see the issue:
1) Create a custom Oracle datatype that represents the database columns that you want to retrieve:
CREATE TYPE my_object AS OBJECT
(COL1 VARCHAR2(50),
COL2 VARCHAR2(50),
COL3 VARCHAR2(50));
2) Create another datatype that is a table of the object you just created:
TYPE MY_OBJ_TABLE AS TABLE OF my_object;
3) Create a function that returns this table. Also use a pipeline clause so that results are pipelined back to the calling SQL, for example:
CREATE OR REPLACE
FUNCTION MY_FUNC (PXOBJCLASS varchar2)
RETURN MY_OBJ_TABLE pipelined IS
TYPE ref1 IS REF CURSOR
Cur1 ref1,
out_rec_my_object := my_object(null,null,null);
myObjClass VARCHAR2(50);
BEGIN
myObjClass := PXOBJCLASS
OPEN Cur1 For ‘select PYID, PXINSNAME, PZINSKEY from PC_WORK where PXOBJCLass = ;1’USING myObjClass,
LOOP
FETCH cur1 INTO out_rec.COL1, out_rec.COL2, out_rec.COL3;
EXIT WHEN Cur1%NOTFOUND;
PIPE ROW (out_rec);
END LOOP;
CLOSE Cur1;
RETURN;
END MY_FUNC;
NOTE: In the example above, you can easily replace the select statement with a call to another stored procedure that returns a cursor variable.
4) In your application, call this function as a table function using the following SQL statement:
select COL1, COL2, COL3 from TABLE(MY_FUNC('SomeSampletask'));
There is no need to use dynamic sql (dynamic sql is always a little bit slower) and there are too many variables declared. Also the for loop is much easier. I renamed the argument of the function from pxobjclass to p_pxobjclass.
Try this:
create or replace function my_func (p_pxobjclass in varchar2)
return my_obj_table pipelined
is
begin
for r_curl in (select pyid,pxinsname,pzinskey
from pc_work
where pxobjclass = p_pxobjclass) loop
pipe row (my_object(r_curl.pyid,r_curl.pxinsname,r_curl.pzinskey));
end loop;
return;
end;
EDIT1:
It is by the way faster to return a ref cursor instead of a pipelined function that returns a nested table:
create or replace function my_func2 (p_pxobjclass in varchar2)
return sys_refcursor
is
l_sys_refcursor sys_refcursor;
begin
open l_sys_refcursor for
select pyid,pxinsname,pzinskey
from pc_work
where pxobjclass = p_pxobjclass;
return l_sys_refcursor;
end;
This is faster because creating objects (my_object) takes some time.
I see two problems:
The dynamic query does not work that way, try this:
'select PYID, PXINSNAME, PZINSKEY from PC_WORK where PXOBJCLass ='''||PXOBJCLASS||''''
You don't need myObjClass, and it seems all your quotes are wrong.
The quoting on 'SomeSampletask'...
select COL1, COL2, COL3 from TABLE(MY_FUNC('SomeSampletask'));
Maybe I'm misunderstanding something here, but it seems like you want to be using a VIEW.