PL SQL : custom data type declaration of the type of this expression is incomplete or malformed function - sql

I have create a package with a function like this :
create or replace
PACKAGE TRANSAC_ERRONNEES AS
TYPE dateArrayVar IS TABLE OF DATE;
FUNCTION calc_date_moyenne(dateArrayIn dateArrayVar
) RETURN DATE;
END TRANSAC_ERRONNEES;
Body :
CREATE OR REPLACE PACKAGE BODY TRANSAC_ERRONNEES AS
FUNCTION calc_date_moyenne(dateArrayIn datearrayvar) RETURN DATE IS
dateFinal DATE;
tempsEnSeconde NUMBER;
tempsMoyenEnSeconde NUMBER;
BEGIN
tempsEnSeconde := 0;
tempsMoyenEnSeconde := 0;
FOR i IN 1..dateArrayIn.count loop
tempsEnSeconde := to_number(to_char(dateArrayIn(i), 'HH24')) * 60 * 60 + to_number(to_char(dateArrayIn(i), 'MI')) * 60 + to_number(to_char(dateArrayIn(i), 'SS'));
tempsMoyenEnSeconde := tempsMoyenEnSeconde+tempsEnSeconde;
end loop;
tempsMoyenEnSeconde := tempsMoyenEnSeconde/dateArrayIn.count;
dateFinal := to_date(TO_CHAR(dateArrayIn(1),'DD-MM-YYYY') || ' ' || TO_CHAR(to_date(tempsMoyenEnSeconde,'sssss'), 'HH24:MI:SS'), 'DD-MM-YYYY HH24:MI:SS');
dbms_output.put_line(TO_CHAR(dateFinal,'DD-MM-YYYY HH24:MI:SS'));
RETURN dateFinal;
END calc_date_moyenne;
END TRANSAC_ERRONNEES;
/
I try to test this function like this :
DECLARE
dates dateArrayVar;
resultat DATE;
BEGIN
dates := dateArrayVar(SYSDATE, SYSDATE);
resultat := transac_erronnees.calc_date_moyenne(dates);
END;
I'm getting this following error :
PLS-00320: the declaration of the type of this expression is
incomplete or malformed
Please let me know the error. Thanks in advance.

You are using it in incorrect way. You need to use array with extend to assign memory and then assign value in it.
DECLARE
dates dateArrayVar;
resultat DATE;
BEGIN
dates.extend(2); -- Since you want to assign two values, extend by 2
dates(1) := SYSDATE;
dates(2) := SYSDATE;
resultat := transac_erronnees.calc_date_moyenne(dates);
END;
Update: Second thing is that DateArrayVar has scope only within the package that you have created, in the anonymous block, Oracle cannot see that declaration. One solution is to create external type that is visible to all other procedures and anonymous block or to test, create a procedure within the package and call it from out side.
create or replace
PACKAGE TRANSAC_ERRONNEES AS
TYPE dateArrayVar IS TABLE OF DATE;
FUNCTION calc_date_moyenne(dateArrayIn dateArrayVar
) RETURN DATE;
PROCEDURE test;
END TRANSAC_ERRONNEES;
/
CREATE OR REPLACE PACKAGE BODY TRANSAC_ERRONNEES AS
FUNCTION calc_date_moyenne(dateArrayIn datearrayvar) RETURN DATE IS
dateFinal DATE;
tempsEnSeconde NUMBER;
tempsMoyenEnSeconde NUMBER;
BEGIN
tempsEnSeconde := 0;
tempsMoyenEnSeconde := 0;
FOR i IN 1..dateArrayIn.count loop
tempsEnSeconde := to_number(to_char(dateArrayIn(i), 'HH24')) * 60 * 60 + to_number(to_char(dateArrayIn(i), 'MI')) * 60 + to_number(to_char(dateArrayIn(i), 'SS'));
tempsMoyenEnSeconde := tempsMoyenEnSeconde+tempsEnSeconde;
end loop;
tempsMoyenEnSeconde := tempsMoyenEnSeconde/dateArrayIn.count;
dateFinal := to_date(TO_CHAR(dateArrayIn(1),'DD-MM-YYYY') || ' ' || TO_CHAR(to_date(tempsMoyenEnSeconde,'sssss'), 'HH24:MI:SS'), 'DD-MM-YYYY HH24:MI:SS');
dbms_output.put_line(TO_CHAR(dateFinal,'DD-MM-YYYY HH24:MI:SS'));
RETURN dateFinal;
END calc_date_moyenne;
PROCEDURE test
dates dateArrayVar;
resultat DATE;
BEGIN
dates.extend(2); -- Since you want to assign two values, extend by 2
dates(1) := SYSDATE;
dates(2) := SYSDATE;
resultat := transac_erronnees.calc_date_moyenne(dates);
END;
END TRANSAC_ERRONNEES;
/
And to test, call it as
BEGIN
TRANSAC_ERRONNEES.TEST;
END;
/

DateArrayVar has scope only within the package
I found the solution : I had to specifiate the package : TRANSAC_ERRONNEES.dateArrayVar;
So :
DECLARE
dates TRANSAC_ERRONNEES.dateArrayVar;
resultat DATE;
BEGIN
dates := TRANSAC_ERRONNEES.dateArrayVar(SYSDATE, SYSDATE);
resultat := transac_erronnees.calc_date_moyenne(dates);
dbms_output.put_line(TO_CHAR(resultat,'DD-MM-YYYY HH24:MI:SS'));
END;
It's works now, Thanks

Related

Change from Default Date to preferred date in this DBMS_SQL?

This code is from the proposed solution:
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:9541646100346616701
My query is 'select * from table'
The second column is a DATE and this routine writes it as appears in my IDE session ('DD-MON-YY');
How can I report the values in DATE columns to 'YYYY-MM-DD'?
procedure clob_maker(p_query varchar2) is
l_theCursor integer default dbms_sql.open_cursor;
l_columnValue varchar2(4000);
l_status integer;
l_descTbl dbms_sql.desc_tab;
l_colCnt number;
n number := 0;
l_data clob;
begin
dbms_sql.parse( l_theCursor, p_query, dbms_sql.native );
dbms_sql.describe_columns( l_theCursor, l_colCnt, l_descTbl );
for i in 1 .. l_colCnt loop
dbms_sql.define_column(l_theCursor, i, l_columnValue, 4000);
end loop;
l_status := dbms_sql.execute(l_theCursor);
while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
for i in 1 .. l_colCnt loop
dbms_sql.column_value( l_theCursor, i, l_columnValue );
--dbms_output.put_line(l_columnValue); --this puts every column on a separate line
l_data := l_data || l_columnValue ||',';
end loop;
dbms_output.put_line(l_data);
n := n + 1;
end loop;
--dbms_output.put_line(l_data);
end clob_maker;
clob_maker('select * from mlb_catcher_birthdays fetch first 10 rows only');
The default format in which dates are represented is controlled by NLS parameter ns_date_format. You can just change this parameter at session level:
alter session set nls_date_format = 'YYYY-MM-DD';
You could also use TO_CHAR(), with the same format specifier as second argument - but this does not fit very well to your use case, since you would then need to check the datatype of each column before printing its value.

PL/SQL Function returning wrong result

I've a PL/SQL function below that's returning a wrong result in SQL Navigator and SQL Developer, but is returning the right answer in SQL Plus.
We've a script running that's executing it and returning the wrong answer too, so trying to fix it. Can anyone see any issues with it? It works fine for most people, but I've a few people going into and returning null/nothing in SQL Navigator and Developer. It's not populating l_end_date for them, and thus credits not populating.
Works fine then in SQL Plus for some reason.
create or replace function mis_get_mem_lcr_credits(p_mem_no in number) RETURN number is
--
v_lcr_credit number;
l_mem_no number;
l_start_date date;
l_end_date date;
l_dob date;
l_18th_date date;
--
cursor c1 is
select mem_no, ind_birth_dt
from cd_individual
where mem_no = l_mem_no
and pkg_mem_utils.get_member_age(mem_no,ind_birth_dt) >= 18
and nvl(ind_student_flag,'N') = 'N'
order by mem_no, ind_birth_dt;
--
cursor c2 is
select distinct m_effdt,
m_termdt
from cd$v_member_contracts9 cd1,
cd_member_product_link cd2
where cd1.mem_no = l_mem_no
and cd1.policy_no = cd2.policy_no
and cd1.m_effdt = cd2.mem_product_eff_dt --.2
and (l_18th_date between cd1.m_effdt and cd1.m_termdt OR cd1.m_effdt > l_18th_date)--.3 18 at time of contract effective date
and nvl(cd1.lapsed_to_start,'N') = 'N'
and cd2.product_id not in (14,41,31) -- Exclude No Cover, DentalProtect and HealthProtect
and cd2.product_id NOT IN (select distinct product_id
from cd_product_options
where nvl(allowed_for_lcr,'Y') = 'N')
order by cd1.m_effdt ASC;
--
begin
--
l_mem_no := p_mem_no;
v_lcr_credit := 0;
l_dob := null;
--
for crec in c1 loop
--
l_dob := crec.ind_birth_dt;
--
-- l_18th_date := substr(to_char(l_dob,'DD/MM/YYYY'),0,6)||(substr(to_char(l_dob,'DD/MM/YYYY'),7,4)+18);
if to_char(l_dob) like '29-02%' then
l_18th_date := add_months(to_date(l_dob+1),216 );
else
l_18th_date := add_months(to_date(l_dob), 216);
end if;
--
for crec2 in c2 loop
--
if crec2.m_termdt > sysdate then
--
l_end_date := sysdate;
--
else
--
l_end_date := crec2.m_termdt;
--
end if;
--
if v_lcr_credit = 0 then --earliest contract
--
if l_18th_date between crec2.m_effdt and crec2.m_termdt then
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,l_18th_date);
--
else
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,crec2.m_effdt);
--
end if;
--
else
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,crec2.m_effdt);
--
end if;
--
end loop;
--
end loop;
--
return round(nvl(v_lcr_credit,0));
--
end mis_get_mem_lcr_credits;
/
show errors
spool off
exit
Never, ever use to_date() on a DATE value.
to_date() converts a varchar to a date.
If you call it with a DATE the date value gets converted to a varchar which then gets converted back to a date which it was to begin with - and being subject to the evil implicit data type conversion twice in that process.
The variable l_dob is defined as DATE so you have to change
add_months(to_date(l_dob+1),216 );
...
add_months(to_date(l_dob), 216);
to
add_months(l_dob+1,216);
...
add_months(l_dob, 216);
Could be because of different values of
NLS_TERRITORY, NLS_DATE_FORMAT etc. in different environments.
So I would suggest to set explicitly these values in your script. e.g. something like EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_TERRITORY=''AMERICA''';
Some References:
NLS_DATE_FORMAT
NLS_TERRITORY

Oracle PL/SQL: Error returned without value on function

I'm trying to create a function that will compute for factorial but it returns an error when I do a SELECT FACTORIAL('1') FROM DUAL;
It returns the heinous error:function returned without value. I tried adding an exception but it seems that it doesn't work either. Care to help?
CREATE OR REPLACE FUNCTION FACTORIAL(p_factorial INTEGER)
RETURN NUMBER
AS var_fnumber number(2);
ctr number(2);
var_contain number(2) := 1;
BEGIN
FOR ctr in 1..p_factorial
LOOP
BEGIN
var_contain := var_contain * ctr;
DBMS_OUTPUT.put_line(var_contain);
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
END;
END LOOP;
END; --FACTORIAL;
/
You MUST return something in a function.
SQL> CREATE OR REPLACE
2 FUNCTION FACTORIAL(
3 p_factorial INTEGER)
4 RETURN NUMBER
5 AS
6 var_fnumber NUMBER(2);
7 ctr NUMBER(2);
8 var_contain NUMBER(2) := 1;
9 BEGIN
10 FOR ctr IN 1..p_factorial
11 LOOP
12 BEGIN
13 var_contain := var_contain * ctr;
14 END;
15 END LOOP;
16 RETURN var_contain;
17 END;
18 /
Function created.
SQL>
SQL> SELECT factorial(2) FROM dual
2 /
FACTORIAL(2)
------------
2
For more details, read http://lalitkumarb.wordpress.com/2014/05/01/ora-06503-plsql-function-returned-without-value/
CREATE OR REPLACE FUNCTION FACTORIAL(p_factorial INTEGER)
RETURN NUMBER
AS var_fnumber number;
ctr number;
var_contain number := 1;
BEGIN
FOR ctr in 1..p_factorial
LOOP
BEGIN
var_contain := var_contain * ctr;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
RETURN var_contain;
END;
END LOOP;
RETURN var_contain;
END; --FACTORIAL;
/
Was gonna say that I found it. Thank you for the answers.
As for the reason why I used varchar2, I wasn't finalizing it yet.
modified your code try this:
create or replace
FUNCTION FACTORIAL(p_factorial INTEGER)
RETURN VARCHAR2
AS
var_contain varchar2(50):= 1;
BEGIN
FOR ctr in 1..p_factorial
LOOP
var_contain := var_contain * ctr;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
RETURN O;
return var_contain;

PLS-00306: when calling a function from a PL/SQL block

I am getting the PLS-00306 error when I attempt to run a PL/SQL block that calls a variety of things including a function. The job of the function is to count how many cars belong to a certain model type. The function works if I call it in a SQL statement or it's own block, it just doesn't seem to work here.
This is the function:
CREATE OR REPLACE Function findtotalcarmodels(
model_name_in IN varchar2)
RETURN NUMBER
IS
counter NUMBER := 0;
CURSOR car_count_cur IS
SELECT model_name
FROM i_car
WHERE model_name = model_name_in;
Rec_car_details car_count_cur%ROWTYPE;
BEGIN
OPEN car_count_cur;
LOOP
FETCH car_count_cur INTO Rec_car_details;
EXIT WHEN car_count_cur%NOTFOUND;
counter := counter + 1;
END LOOP;
CLOSE car_count_cur;
RETURN counter;
END;
This is the Block:
SET SERVEROUTPUT ON FORMAT WRAP SIZE 12000
Declare
v_model VARCHAR2(40);
v_carcategory VARCHAR2(40);
v_totalcars NUMBER;
v_maxdate DATE:=TO_DATE(1, 'J');
Cursor carcur IS
SELECT *
FROM i_car;
CURSOR c1(v_car_registration VARCHAR2) IS
SELECT *
from i_booking a
WHERE a.registration=v_car_registration;
Begin
For car_rec in carcur
LOOP
v_maxdate:=TO_DATE(1, 'J');
for rec in c1(car_rec.registration)
loop
IF rec.date_reserved > v_maxdate
then
v_maxdate:=rec.date_reserved ;
If car_rec.Cost <=50000
THEN
v_carcategory := 'Budget Car';
End IF;
If car_rec.Cost BETWEEN 50000 AND 100000
THEN
v_carcategory := 'Standard Car';
End IF;
If car_rec.Cost >100000
THEN
v_carcategory := 'Premium Car';
End If;
end IF;
v_totalcars := findtotalcarmodels;
end loop;
DBMS_OUTPUT.PUT_LINE('Registration:'|| ' '|| car_rec.registration);
DBMS_OUTPUT.PUT_LINE('Cost:'|| ' $' || car_rec.Cost);
DBMS_OUTPUT.PUT_LINE('Model Name:'|| ' '|| car_rec.model_name);
DBMS_OUTPUT.PUT_LINE('Car Category:'|| ' '||v_carcategory);
DBMS_OUTPUT.PUT_LINE('Total number of Cars:'|| ' '||v_totalcars);
DBMS_OUTPUT.PUT_LINE('Most Recent Rental Date: '|| ' '||v_maxdate);
DBMS_OUTPUT.NEW_LINE;
END LOOP;
END;
/
Before I get slammed for the style of the PL/SQL block, just keep in mind that it is written to requirement and everything works well with the exception of the function.
If someone could point me in the right direction to call this function without error I would be very grateful.
It looks like you have missed to pass the IN parameter to the function.
Try like this,
v_totalcars := findtotalcarmodels('<model_name_in>');
Well, when i see things right, you don't fillup the parameter model_name. When you don't have an overriden function wihtout parameter you need to fill it up.
AS you can also see the PLS-00306 is telling you something about wrong number of arguments.

how can we get the value of of VARRAY from IN Parameter in procedure

I am new to PL/SQL... In Varray how can i get multiple value from IN parameter..... else Is there another ways to get the values...
I want to interate the values through VArray... if any other options then its fine..
coding:
CREATE OR REPLACE
PROCEDURE dynamic_query_build(
vr_plan_sku_id IN VARCHAR2 )
IS
type plan_sku_id_array IS VARRAY(999) OF VARCHAR2(5000);
plan_sku_id plan_sku_id_array;
total INTEGER;
vrx_plan_sku_id VARCHAR2(3000);
BEGIN
vrx_plan_sku_id:= REPLACE(vr_plan_sku_id,',',chr(39)||','||chr(39));
plan_sku_id := plan_sku_id_array(chr(39)||vrx_plan_sku_id||chr(39));
total := plan_sku_id.count;
FOR i IN 1 .. total
LOOP
dbms_output.put_line(plan_sku_id(i));
END LOOP;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END dynamic_query_build;
Execution:
set serveroutput on;
declare
vr_plan_sku_id varchar2(200) := '5863314,5863315';
BEGIN
dynamic_query_build(vr_plan_sku_id);
END;
/
My Output:
anonymous block completed
'5863314','5863315'
Expected output:
5863314
5863315
now it is considering as single value....
I created anonymous block with procedure dynamic_query_build. Added there code, that will split VARCHAR2 variable into varray.
I think, the key to your question is this line - plan_sku_id.EXTEND();
You can extend varray dynamically, but only till it reaches defined maximum (in your case - 999).
DECLARE
vr_plan_sku_id varchar2(200) := '5863314,5863315';
PROCEDURE dynamic_query_build(
vr_plan_sku_id IN VARCHAR2 )
IS
type plan_sku_id_array IS VARRAY(999) OF VARCHAR2(5000);
plan_sku_id plan_sku_id_array;
total INTEGER;
position PLS_INTEGER := 0;
last_position PLS_INTEGER := 1;
tmp VARCHAR2(5000);
counter PLS_INTEGER := 1;
BEGIN
plan_sku_id := plan_sku_id_array();
LOOP
position := INSTR(vr_plan_sku_id, ',', last_position);
IF position > 0 THEN
tmp := SUBSTR(vr_plan_sku_id, last_position, position - last_position);
last_position := position + 1;
ELSE
tmp := SUBSTR(vr_plan_sku_id, last_position);
END IF;
plan_sku_id.EXTEND();
plan_sku_id(counter) := tmp;
counter := counter + 1;
EXIT WHEN position = 0 OR counter > 10;
END LOOP;
total := plan_sku_id.count;
FOR i IN 1 .. total
LOOP
dbms_output.put_line(plan_sku_id(i));
END LOOP;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END dynamic_query_build;
BEGIN
dynamic_query_build(vr_plan_sku_id);
END;
/
Put a replace in the dbms_output statement this will eliminate the quotes from the string
....
dbms_output.put_line replace (replace (plan_sku_id(i), '''' ))',',chr(10);
.....