returning query in oracle function - oracle

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;

Related

How can I test my package with one function?

I made a package that compiles fine but when I try to test it it gives me "invalid data type".
I've tried two different ways, first one like this
select pkg_contabilidad.f_totalizar_Detalle(100) FROM DUAL;
It gives me the ORA-00902 'invalid data type'
Also I've tried this
DECLARE
TYPE r_registro IS RECORD
(rubro_contable CN_RUBROS_CONTABLES.COD_RUBRO%TYPE,
tipo VARCHAR2(1),
monto NUMBER(16));
resultao r_registro;
numero NUMBER :=100;
BEGIN
resultao := pkg_contabilidad.f_totalizar_detalle(numero);
END;
It gives me another error PLS-00382 'expression is of wrong type'
I don't know what am I doing wrong, cause my function receives just one parameter and is of type NUMBER, so I dont know where's my mistake. I'll leave the code of my package just in case
CREATE OR REPLACE PACKAGE pkg_contabilidad AS
TYPE r_registro IS RECORD
(rubro_contable CN_RUBROS_CONTABLES.COD_RUBRO%TYPE,
tipo VARCHAR2(1),
monto NUMBER(16));
TYPE t_detalle IS TABLE OF
r_registro INDEX BY BINARY_INTEGER;
FUNCTION f_totalizar_detalle(p_clave NUMBER)RETURN t_detalle;
END pkg_contabilidad;
/
CREATE OR REPLACE PACKAGE BODY pkg_contabilidad AS
B_detalle t_detalle;
i integer :=1;
FUNCTION f_totalizar_detalle(p_clave NUMBER) RETURN t_detalle IS
v_detalle t_detalle;
CURSOR c_facturado IS
SELECT c.cod_rubro, 'H', CASE WHEN SUM(d.gravada)=0 THEN SUM(d.iva) ELSE SUM(d.gravada) END
FROM fn_documentos_det d JOIN fn_conceptos c ON d.cod_concepto = c.cod_concepto
WHERE d.clave_doc=p_clave
GROUP BY c.cod_rubro;
CURSOR c_datos IS
SELECT SUM(d.total_doc), 'D',r.cod_rubro
FROM fn_documentos d JOIN fn_cajas_ctas r ON d.num_caja_cta = r.num_caja_cta
WHERE d.clave_doc = p_clave
GROUP BY r.cod_rubro;
BEGIN
open c_datos;
LOOP
FETCH c_datos INTO v_detalle(1);
END LOOP;
CLOSE c_datos;
FOR fila IN c_facturado LOOP
i := i + 1;
v_detalle(i) := fila;
END LOOP;
END;
END PKG_CONTABILIDAD;
The function returns a pkg_contabilidad.t_detalle, so the test needs to be:
declare
resultao pkg_contabilidad.t_detalle;
numero number := 100;
begin
resultao := pkg_contabilidad.f_totalizar_detalle(numero);
end;
It doesn't work in SQL because pkg_contabilidad.t_detalle is a PL/SQL type, not a SQL type (create or replace type). The database can perform some automatic conversions, but there are still limitations.
By the way, this loop will never complete because it lacks an exit condition:
open c_datos;
loop
fetch c_datos into v_detalle(1);
end loop;
close c_datos;
Your function returns a PL/SQL table type, with a table of a PL/SQL record type, which is defined in your package, which plain SQL doesn't know about and can't display - hence your invalid datatype error. If you need to call the function and access the data from SQL you can create schema-level object and collection types instead.
In your anonymous block you are a declaring a new record type. That looks the same to you because the structure is the same, but Oracle expects the exact type the function returns. That makes your test code shorter and simpler though. But you are also trying to return the whole collection into a single record.
DECLARE
l_detalle pkg_contabilidad.t_detalle;
l_registro pkg_contabilidad.r_registro;
l_idx pls_integer;
numero NUMBER :=100;
BEGIN
l_detalle := pkg_contabilidad.f_totalizar_detalle(numero);
l_idx := l_detalle.FIRST;
WHILE l_idx is not null LOOP
l_registro := l_detalle(l_idx);
-- do something with this record
dbms_output.put_line(l_registro.tipo);
l_idx := l_detalle.NEXT(l_idx);
END LOOP;
END;
db<>fiddle with dummy cursors.
Your function is a bit strange and probably isn't doing quite what you want; but also has two fatal problems: it isn't returning anything, and it has an infinite loop. I've fixed those for the fiddle but not anything else, as this seems to be an exercise.

ORA-06575: is in an invalid state

Hello I am attempting to do the following problem and keep receiving errors. Create a user-defined function called DOLLARS_BY_VEHICLE_TYPE that receives an input parameter of a concatenated make and model and then queries the VEHICLES and SALES_FACTS tables to return the total gross sales amount of the sales by that combination. My code is as follows:
CREATE OR REPLACE FUNCTION DOLLARS_BY_VEHICLE_TYPE (V_AUTODESC VARCHAR2)
RETURN VARCHAR2
IS
V_AMT VARCHAR2 (50);
BEGIN
SELECT NVL (SUM (GROSS_SALES_AMOUNT), 0)
INTO V_AMT
FROM VEHICLES v, SALES_FACTS s
WHERE V.Vehicle_Code = S.Vehicle_Code AND V.DESCRIPTION= V_AUTODESC;
RETURN V_AMT;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END;
/
However, when I run it I get:
Warning: Function created with compilation errors.
ERROR at line 1:
ORA-06575: Package or function DOLLARS_BY_VEHICLE_TYPE is in an invalid state
Please see that you are trying to return the sum from your function and that can be only number/int/float. So the return type of your function should be number not varchar2. Also note that you are using a deprecated sql syntax which can be changed. See below:
CREATE OR REPLACE FUNCTION DOLLARS_BY_VEHICLE_TYPE (V_AUTODESC VARCHAR2)
-- RETURN VARCHAR2
RETURN NUMBER
IS
--V_AMT VARCHAR2 (50);
V_AMT NUMBER;
BEGIN
SELECT NVL (SUM (GROSS_SALES_AMOUNT), 0)
INTO V_AMT
FROM VEHICLES v
inner join SALES_FACTS s
ON V.Vehicle_Code = S.Vehicle_Code
where V.DESCRIPTION = V_AUTODESC;
RETURN V_AMT;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END;
/

How to use Pl/SQL Collection within a Function

For an assignment the recommendation was to use collections. Simply said I need to chop a string in several substrings, reverse the order of those strings and put them all together.
Omitted actual code in because it crashes at the declare part.
Keep getting the error: stringlist1 is not a procedure or is undefined
So for some reason the type I try to assign to stringlist1 isn't doing it.
Do you guys know why this is happening? And if this isn't fixable what is a neat workaround other than making a variable for every row in the collection?
Things I tried:
* Create my own type in a "create type" statement beforehand
* Creating the type in an anonymous block before the function
* Creating a varchar collection within the declare part of the function
* The dbms package used below
* Renaming the hell out of it
* Trying to initiate the type like stringlist1 dbms_utility.name_array := dbms_utility.name_array();
Create or replace FUNCTION TEST (p_number varchar2)
RETURN varchar2
IS
stringlist1 dbms_utility.name_array;
stringlist2 dbms_utility.name_array;
BEGIN
stringlist1('test');
stringlist2('test');
dbms.output.put_line(stringlist1(1));
return stringlist1(1);
END;
String can be added to array like stringlist1(1) := 'test';
So your code will be compile when corrected like
CREATE OR REPLACE FUNCTION TEST (p_number VARCHAR2)
RETURN VARCHAR2
IS
stringlist1 DBMS_UTILITY.name_array;
stringlist2 DBMS_UTILITY.name_array;
BEGIN
stringlist1(1) := 'test';
stringlist2(1) := 'test';
dbms_output.put_line (stringlist1 (1));
RETURN stringlist1 (1);
END;
PS: Package name is not dmbs.output, correct one is dbms_output.

Pass the result of table(xmlsequence()) to a function as a parameter

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.

How to debug a user defined aggregate function in Oracle 11g?

I'm trying to learn how to create a user defined aggregate function. So far, I've been able to create one that compiles fine, but calling it gives an unexpected result. The function is a very simple test function that looks through a number of rows that are either set to 'Y' or 'N' and returns 'Y' if all are set to 'Y' and otherwise returns 'N'. I'm running it on a single row and getting back a blank varchar 2 instead.
I'm not sure what is the procedure to go through with debugging this. I've tried using DBMS_OUTPUT.PUT_LINE(), but I cannot see anything on the database output. The largest problem is that it is creating the function fine, and most of the code is in an object type. Thus, if I were to try to debug the select statement, it is calling code on the database that has already been compiled.
Below is the code for the function, but I don't want to know why this isn't working as much as I want to know how to debug so I can solve these issues myself, especially when more complex aggregate functions are involved.
CREATE OR REPLACE TYPE MYSCHEMA.ALL_TRUE_T AS OBJECT
(
TRUE_SO_FAR VARCHAR2(1),
STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT ALL_TRUE_T) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT ALL_TRUE_T, value IN VARCHAR2) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(self IN ALL_TRUE_T, returnValue OUT VARCHAR2, flags IN NUMBER) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(self IN OUT ALL_TRUE_T, ctx2 IN ALL_TRUE_T) RETURN NUMBER
);
CREATE OR REPLACE TYPE BODY MYSCHEMA.ALL_TRUE_T IS
STATIC FUNCTION ODCIAggregateInitialize(sctx IN OUT ALL_TRUE_T)
RETURN NUMBER IS
BEGIN
sctx := ALL_TRUE_T('Y');
return ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT ALL_TRUE_T, value IN VARCHAR2)
RETURN NUMBER IS
BEGIN
IF value <> 'Y' OR self.TRUE_SO_FAR <> 'Y' THEN
self.TRUE_SO_FAR := 'N';
END IF;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate(self IN ALL_TRUE_T, returnValue OUT VARCHAR2, flags IN NUMBER)
RETURN NUMBER IS
BEGIN
returnValue := self.TRUE_SO_FAR;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge(self IN OUT ALL_TRUE_T, ctx2 IN ALL_TRUE_T)
RETURN NUMBER IS
BEGIN
IF ctx2.TRUE_SO_FAR = 'N' THEN
self.TRUE_SO_FAR := 'N';
END IF;
RETURN ODCIConst.Success;
END;
END;
CREATE OR REPLACE PACKAGE MYSCHEMA.ALL_TRUE_PKG IS
FUNCTION ALL_TRUE (input VARCHAR2) RETURN VARCHAR2;
END;
CREATE OR REPLACE PACKAGE BODY MYSCHEMA.ALL_TRUE_PKG IS
FUNCTION ALL_TRUE (input VARCHAR2) RETURN VARCHAR2
AGGREGATE USING ALL_TRUE_T;
END;
And here is how I call it. YN_TEST_TABLE currently has a single row with an 'N' in it.
SELECT
MYSCHEMA.ALL_TRUE_PKG.ALL_TRUE(YN)
FROM
MYSCHEMA.YN_TEST_TABLE
Finally, I'm not sure if this is relevant, but I'm using Toad 11.6.
Edit:
So I've tried inserting into a temp log table and that didn't work either.
I added the following
MEMBER FUNCTION ODCIAggregateIterate(self IN OUT ALL_TRUE_T, value IN VARCHAR2)
RETURN NUMBER IS
BEGIN
BEGIN
INSERT INTO MYSCHEMA.LAWTONFOGLES_TEMP_LOG
(
ID,
Message,
Time
)
VALUES
(
'all_true',
'test1',
systimestamp
);
END;
IF value <> 'Y' OR self.TRUE_SO_FAR <> 'Y' THEN
self.TRUE_SO_FAR := 'N';
END IF;
RETURN ODCIConst.Success;
END;
There was nothing in the temp log, but also no error message. It is as if none of the 4 aggregate function parts are even being called.
EDIT2:
So, to make things more interesting, this works when it is not in a package.
I did the following
CREATE OR REPLACE FUNCTION MYSCHEMA.LAWTONFOGLES_ALL_TRUE (input VARCHAR2) RETURN VARCHAR2
AGGREGATE USING ALL_TRUE_T;
and then ran this
SELECT
MYSCHEMA.LAWTONFOGLES_ALL_TRUE(YN)
FROM
MYSCHEMA.YN_TEST_TABLE
and got the results I expected. It seems that the code itself isn't a problem, but putting it in a package causes it to break. Thursday my Oracle DBA will be opening a ticket up with oracle, so I'll be sure to update with why does putting this in a package break it but leaving it as just a function doesn't when they get back with us. Until then I may just have to keep this outside of a package.
Also, I tried to add a put_line on it when it was working and still did not get an output. I think that the way user defined aggregate functions work prevent put_line from working.
If you're using TOAD, be sure to turn on DBMS_OUTPUT recording before you run your proc so you can see your outputs. It should be on the bottom DBMS tab (if you have it open). Typically you'll see a red circle since it's defaulted as off. Click the circle so that it's green.
See this link as an example: http://geekbrigade.wordpress.com/2009/04/09/how-to-set-and-view-dbms-output-of-oralce-in-toad/

Resources