ORACLE SQL Method Produces Warning - sql

I'm having trouble getting the following member method to compile (count_single_buses). Would appreciate any advice on what might be wrong syntactically with my code.
CREATE OR REPLACE TYPE BodyModel2_Type AS OBJECT(
ModelID INTEGER,
ModelName VARCHAR2(45),
FloorType VARCHAR2(45),
Manufacturer VARCHAR2(45),
Length NUMBER(8,2),
Width NUMBER(8,2),
NoOfAxles INTEGER,
MEMBER FUNCTION count_single_buses(ModelID INTEGER) RETURN INTEGER);
/
CREATE OR REPLACE TYPE BODY BodyModel2_Type AS
MEMBER FUNCTION count_single_buses(ModelID INTEGER) RETURN INTEGER IS
N INTEGER;
BEGIN
N := (SELECT COUNT(BODYMODELREF) FROM SINGLEDECKBUS_TABLE S
WHERE S.BODYMODELREF = ModelID);
RETURN N;
END count_single_buses;
END;
--EDIT--
Thanks to #Ravi, I managed to solve the issue my correcting my SQL syntax and setting the resultset to a NUMBER, instead of INTEGER.
CREATE OR REPLACE TYPE BODY BodyModel_Type AS
MEMBER FUNCTION count_single_buses(thisModelID INTEGER) RETURN NUMBER IS
NUM NUMBER;
BEGIN
SELECT COUNT(S.BODYMODELREF) INTO NUM FROM SINGLEDECKBUS_TABLE S WHERE S.BODYMODELREF.MODELID = thisModelID;
RETURN NUM;
END count_single_buses;
END;
/
Still not sure why #Ravi's exact code still produced the warning, and thought that resultset when returning a count value could go into an integer. At any rate, the code works now. Thanks all.

Your BodyModel2_Type Type definition looks okay. However, the body definition is syntactically incorrect.
You cannot define a SQL statement directly to a variable, thus making this statement wrong.
N := (SELECT COUNT(BODYMODELREF) FROM SINGLEDECKBUS_TABLE S
WHERE S.BODYMODELREF = ModelID);
You will have to use Select... into statement in order to assign the result set of your SQL query into a variable. So, the right syntax should look like this
SELECT COUNT(BODYMODELREF) FROM SINGLEDECKBUS_TABLE S INTO N
WHERE S.BODYMODELREF = ModelID
AFAIK you don't have END the Type followed by the Type name like this END count_single_buses. It'll produce an error. So, overall your Type body specification should look like this:
CREATE OR REPLACE TYPE BODY BodyModel2_Type AS
MEMBER FUNCTION count_single_buses(ModelID INTEGER) RETURN NUMBER IS
N NUMBER;
BEGIN
SELECT COUNT(BODYMODELREF) FROM SINGLEDECKBUS_TABLE S INTO N
WHERE S.BODYMODELREF = ModelID;
RETURN (N);
END;
END;
/
I'm writing this off without any live environment available right now so please let me know if you come across any error in the above code.
Cheers.

Related

Passing char value to function

I have the following function:
CREATE OR REPLACE FUNCTION BANINST1."F_COC_AUTO_AWARD_FILTER" (pidm number) return number
as
return_field number;
cursor get_pidm is
select distinct SHRDGMR.SHRDGMR_PIDM
from SATURN.SHRDGMR SHRDGMR,
SATURN.SORLCUR SORLCUR,
SATURN.SORLFOS SORLFOS,
DWSCHEMA.DAP_AUDIT_DTL#LINKDWTEST
where SORLCUR.SORLCUR_PIDM = SHRDGMR.SHRDGMR_PIDM
and SORLFOS.SORLFOS_PIDM = SORLCUR.SORLCUR_PIDM
and SORLCUR.SORLCUR_LEVL_CODE = SHRDGMR.SHRDGMR_LEVL_CODE
and SORLCUR.SORLCUR_DEGC_CODE = SHRDGMR.SHRDGMR_DEGC_CODE
and SORLFOS.SORLFOS_TERM_CODE = SORLCUR.SORLCUR_TERM_CODE
and SHRDGMR.SHRDGMR_PIDM = pidm
and SHRDGMR.SHRDGMR_DEGS_CODE = 'AW'
and SORLCUR.SORLCUR_PROGRAM in ('STCC', 'CC')
and DWSCHEMA.DAP_AUDIT_DTL.DAP_DEGREE in ('CPCC-CDS', 'CC1-CDS', 'CC2-CDS')
and SORLFOS.SORLFOS_MAJR_CODE <> DWSCHEMA.DAP_AUDIT_DTL.DAP_AUD_VALUE2
and trim(DWSCHEMA.DAP_AUDIT_DTL.DAP_STU_ID) = (select spriden_id from spriden where spriden_pidm = pidm and spriden_change_ind is null);
begin
open get_pidm;
fetch get_pidm into return_field;
close get_pidm;
return return_field;
end;
/
This is the call to the function from a where clause:
baninst1.f_coc_auto_award_filter#test(RAD_PRIMARY_MST.RAD_USER_DEF1) is not null
The function accepts a number data type parameter.
The passed column RAD_PRIMARY_MST.RAD_USER_DEF1 is a char(12) data type that has a value such as 293858.
The following error is returned when calling the function: ORA-01722: invalid number
I have tried to pass a number value to the function in several different ways:
baninst1.f_coc_auto_award_filter#test(to_number(trim(RAD_PRIMARY_MST.RAD_USER_DEF1))) is not null
baninst1.f_coc_auto_award_filter#test(cast(RAD_PRIMARY_MST.RAD_USER_DEF1 as number(8))) is not null
All attempts return the same error ORA-01722: invalid number
If I hard-code the D_PRIMARY_MST.RAD_USER_DEF1 value to a number, the function call works.
baninst1.f_coc_auto_award_filter#test(293858) is not null
How do I pass the char value from RAD_PRIMARY_MST.RAD_USER_DEF1 to the function?
This might be the keyword:
such as 293858
Are you sure that there aren't any non-numeric values in that column? With that datatype (char), there's always doubt. If you are storing numbers in there, why isn't it number?
This query should return invalid values which cause your code to break.
select * from d_primary_mst where not regexp_like(rad_user_def1, '^\d+$')
Also, if you are passing a string, why do you force the function to accept a number? Why don't you
CREATE OR REPLACE FUNCTION BANINST1.F_COC_AUTO_AWARD_FILTER
(pidm RAD_PRIMARY_MST.RAD_USER_DEF1%type)
return SHRDGMR.SHRDGMR_PIDM%type
as
return_field SHRDGMR.SHRDGMR_PIDM%type;
cursor get_pidm is
select distinct SHRDGMR.SHRDGMR_PIDM ...
By the way, get rid of double quotes when creating Oracle objects; they only cause problems.

How to compare two oracle sql object type with common base super class

Example:
Figure_t base class (super class)
sphere_t under figure_t
pyramid_t under figure_t
both has volume.
How to do objects comparison using map or order function?
What I am doing is using the map member function in the super class for comparing using the volume. I have tried with/without override of the map function in the subclass but still no luck. I can compare if I create the same object twice but not if I create different ones.
In example below I paste just the sphere since it is almost the same for both sphere and pyramid.
This is my super class:
CREATE OR REPLACE TYPE figure_t AS OBJECT (
v_volume NUMBER,
v_area NUMBER,
MAP MEMBER FUNCTION compare RETURN NUMBER, PRAGMA restrict_references ( compare, wnds, trust )
);
/
CREATE OR REPLACE TYPE BODY figure_t AS
MAP MEMBER FUNCTION compare RETURN NUMBER IS
BEGIN
RETURN v_volume;
END;
END;
/
ALTER TYPE figure_t NOT FINAL
CASCADE;
/
Then, this is my subtype:
CREATE OR REPLACE TYPE sphere_t UNDER figure_t (
v_radio NUMBER,
CONSTRUCTOR FUNCTION sphere_t (
radio NUMBER
) RETURN SELF AS RESULT,
MEMBER FUNCTION get_volume RETURN NUMBER,
MEMBER FUNCTION get_area RETURN NUMBER,
OVERRIDING MAP MEMBER FUNCTION compare RETURN NUMBER
);
/
CREATE OR REPLACE TYPE BODY sphere_t AS
CONSTRUCTOR FUNCTION sphere_t (
radio NUMBER
) RETURN SELF AS RESULT IS
BEGIN
self.v_radio := radio;
self.v_volume := ( 4 / 3 ) * 3.141592654 * power(radio, 3);
self.v_area := 4 * 3.141592654 * power(radio, 2);
return;
END;
MEMBER FUNCTION get_volume RETURN NUMBER IS
BEGIN
RETURN v_volume;
END;
MEMBER FUNCTION get_area RETURN NUMBER IS
BEGIN
RETURN v_area;
END;
OVERRIDING
MAP MEMBER FUNCTION compare RETURN NUMBER IS
BEGIN
RETURN self.v_volume;
END;
END;
/
For doing the comparison it looks like:
DECLARE
sphere_v sphere_t;
pyramid_v pyramid_t;
BEGIN
pyramid_v := pyramid_t(120, 90, 30);
sphere_v := sphere_t(10);
IF ( sphere_v != pyramid_v ) THEN
dbms_output.put_line('NOT EQUAL');
END IF;
END;
There should be a way for this comparison since figures have a super class in common.
There should be a way for this comparison since figures have a super
class in common
Am not very sure as what you wanted to achieve here. Also what is definition of pyramid_t which you are using in your comparison.
IF ( sphere_v != pyramid_v ) THEN
The above condition looks dicey to me as this will always be the case.
When you do sphere_v := sphere_t(10); means you try to get all the return of the sphere_t to sphere_v.
So it would be good if you could compare the volume and area of the Sphere and pyramid separately. See below demo how you could take these value:
DECLARE
sphere_v sphere_t;
-- pyramid_v pyramid_t;
BEGIN
sphere_v := sphere_t(10);
dbms_output.put_line('Input Radio -->'||sphere_v.v_radio);
dbms_output.put_line('Volume of Sphere-->'||sphere_v.v_volume);
dbms_output.put_line('Area Of Sphere -->'||sphere_v.v_area);
--Similarly you can take the values of `volume` and `area`
--of pyramid and get it compared with that of Sphere.
-- pyramid_v := pyramid_t(120, 90, 30);
-- dbms_output.put_line('Input Radio Pyramid -->'||pyramid_v.v_radio);
-- dbms_output.put_line('Volume of Pyramid -->'||pyramid_v.v_volume);
-- dbms_output.put_line('Area Of Pyramid -->'||pyramid_v.v_area);
-- If sphere_v.v_volume = pyramid_v.v_volume then
-- dbms_output.put_line('Equal');
-- Else
-- dbms_output.put_line('Not Equal');
END;
Assumption: pyramid_t also have the same Object Body definition having volume and area calculation.
There should be a way for this comparison since figures have a super class in common.
There is a way, it's just not obvious.
When the types are exactly the same e.g. two instances of the same subtype we can invoke the map function implicitly. So we can compare two spheres like this:
IF ( sphere_1 != sphere_2 ) THEN ...
However, to compare two different subtypes we need to invoke the parent map function, and to make this happen we must reference it explicitly:
IF ( sphere_v.compare() != pyramid_v.compare() ) THEN ...
Yes, this is clunky. But Oracle is an RDBMS not an ORDBMS (whatever they claimed back in the version 8.0 days).

Assist with currency format

Good Day please can someone assist me with a currency format of ###,###,###.## everything I try I seem to be getting stuck - My code is as follows:
function CF_TOTAL_COSTFormula return Number is
total_cost number;
begin
select SUM(share_amount) into total_cost
from reservation_name rn,
reservation_daily_element_name rden
where rn.resv_name_id = rden.resv_name_id
and rn.resv_name_id = :resv_name_id
and trunc(rden.reservation_date) between trunc(rn.begin_date) and trunc(rn.end_date - 1);
return total_cost;
exception when others then
return 0;
end;
Your function returns a NUMBER. Numbers in Oracle have no format. If you want to return a formatted number, you need to return a VARCHAR2. Something like this:
function CF_TOTAL_COSTFormula return varchar2 is
total_cost varchar2(30);
begin
select to_char(SUM(share_amount),'999,999,990.00') into total_cost
from reservation_name rn,
reservation_daily_element_name rden
where rn.resv_name_id = rden.resv_name_id
and rn.resv_name_id = :resv_name_id
and trunc(rden.reservation_date) between trunc(rn.begin_date) and trunc(rn.end_date - 1);
return total_cost;
exception when others then
return 'error';
end;
As an aside, I gather from the name of your function that perhaps you are using Oracle Reports or Oracle BI Publisher. If so, you should continue to return a number and handle the display formatting in the layout, not the model.

Adding Many (UDFs) Validation Functions to Oracle - Which Method Run Fastest

I have to move around 50+ validation functions into Oracle. I'm looking for the approach that runs fastest, but also would like to get around a boolean issue if possible. The return object for them all needs to be the same so that the application can react off the result in a consistent fashion and alert the user or display whatever popups, messages we may need. I created a valObj for this, but not sure yet if that is the best approach. The return format can be changed because the front-end that reacts off of it is not developed yet. In the end it will contain many different validation functions, from integer, number, phone, email, IPv4, IPv6, etc... This is what I have so far...
/***
This is the validation object.
It stores 1 for valid, 0 for not valid and some helper text that can be relayed back to the user.
***/
create or replace type valObj as object (
result number(1),
resultText varchar(32000)
);
/***
Coming from ColdFusion this seems clean to me but the function
will end up being a couple thousand lines long.
***/
create or replace function isValid(v in varchar2, format in varchar2)
return valObj
is
test number;
begin
if format = 'number' then
begin
test := to_number(v);
return valObj(1,null);
exception when VALUE_ERROR then return valObj(0,'Invalid number. Valid formats are: 12345, 12345.67, -12345, etc...');
end;
elsif format = 'integer' then
null; --TO DO
elsif format = 'email' then
null; --TO DO
elsif format = 'IPv4' then
null; --TO DO
elsif format = 'IPv6' then
null; --TO DO
end if;
--dozens of others to follow....
end;
/
/* Example Usage in SQL */
select isValid('blah','number') from dual; -- returns: (0, Invalid number. Valid formats are: 12345, 12345.67, -12345, etc...)
select isValid('blah','number').result from dual; -- returns: 0
select isValid('blah','number').resulttext from dual; -- returns: Valid formats are: 12345, 12345.67, -12345, etc...
select isValid(1234567890.123,'number') from dual; -- returns: 1,{null}
select isValid(1234567890.123,'number').result from dual; -- returns: 1
select isValid(1234567890.123,'number').resulttext from dual; -- returns: {null}
/* Example Usage in PL/SQL */
declare
temp valObj;
begin
temp := isValid('blah','number');
if (temp.result = 0) then
dbms_output.put_line(temp.resulttext);
else
dbms_output.put_line('Valid');
end if;
end;
/
My questions are:
When using it in PL/SQL I would love to be able to do boolean checks instead like this: if (temp.result) then but I can't figure out a way, cause that won't work in SQL. Should I just add a 3rd boolean attribute to the valObj or is there another way I don't know of?
These validation functions could end up being called within large loops. Knowing that, is this the most efficient way to accomplish these validations?
I'd appreciate any help. Thanks!
UPDATE: I forgot about MEMBER FUNCTIONS. Thanks #Brian McGinity for reminding me. So I'd like to go with this method since it keeps the type and its functions encapsulated together. Would there be any speed difference between this method and a stand-alone function? Would this be compiled and stored the same as a stand-alone function?
create or replace type isValid as object (
result number(1),
resulttext varchar2(32000),
constructor function isValid(v varchar, format varchar) return self as result );
/
create or replace type body isValid as
constructor function isValid(v varchar, format varchar) return self as result as
test number;
begin
if format = 'number' then
begin
test := to_number(v);
self.result := 1;
self.resulttext := null;
return;
exception when VALUE_ERROR then
self.result := 0;
self.resulttext := 'Invalid number. Valid formats are: 12345, 12345.67, -12345, etc...';
return;
end;
elsif format = 'phone' then
null; --TO DO
end if;
--and many others...
end;
end;
/
/* Example Usage in SQL */
select isValid('a','number') from dual;
/* Example Usage in PL/SQL */
declare
begin
if (isValid('a','number').result = 1) then
null;
end if;
end;
/
TEST RESULTS:
/* Test isValid (the object member function), this took 7 seconds to run */
declare
begin
for i in 1 .. 2000000 loop
if (isValid('blah','number').result = 1) then
null;
end if;
end loop;
end;
/* Test isValid2 (the stand-alone function), this took 16 seconds to run */
declare
begin
for i in 1 .. 2000000 loop
if (isValid2('blah','number').result = 1) then
null;
end if;
end loop;
end;
Both isValid and isValid2 do the same exact code, they just run this line test := to_number(v); then do the exception if it fails and return the result. Does this appear to be a valid test? The Object member function method is actually faster than a stand-alone function???
The stand-alone function can be much faster if you set it to DETERMINISTIC and if the data is highly repetitive. On my machine this setting decreased run time from 9 seconds to 0.1 seconds. For reasons I don't understand that setting does not improve performance of the object function.
create or replace function isValid2(v in varchar2, format in varchar2)
return valObj
deterministic --<< Hit the turbo button!
is
test number;
begin
if format = 'number' then
begin
test := to_number(v);
return valObj(1,null);
exception when VALUE_ERROR then return valObj(0,'Invalid number. Valid formats are: 12345, 12345.67, -12345, etc...');
end;
end if;
end;
/
May also want to consider utilizing pls_integer over number. Don't know if it will buy you much, but documents suggest some gain will be had.
http://docs.oracle.com/cd/B10500_01/appdev.920/a96624/03_types.htm states,
"You use the PLS_INTEGER datatype to store signed integers. Its magnitude range is -2*31 .. 2*31. PLS_INTEGER values require less storage than NUMBER values. Also, PLS_INTEGER operations use machine arithmetic, so they are faster than NUMBER and BINARY_INTEGER operations, which use library arithmetic. For efficiency, use PLS_INTEGER for all calculations that fall within its magnitude range."

Character string buffer too small error in Oracle Stored Procedure

I am getting an error in an Oracle 11g stored procedure. The error is...
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
It is happening at line 31, the line that contains out_cnt_tot := 0; I'm really not sure why there is anything wrong with that line. Another programmer created this procedure and I'm really not familiar with SQL procedures. Can anyone help me figure this out?
create or replace
PROCEDURE "FIP_BANKREC_PREP"
(
in_file_date in varchar2,
in_bank_code in varchar2,
out_cnt_apx_miss_no out integer,
out_cnt_prx_miss_no out integer,
out_cnt_apx_no_mtch out integer,
out_cnt_prx_no_mtch out integer,
out_cnt_ap_dup out integer,
out_cnt_pr_dup out integer,
out_cnt_bad out integer,
out_cnt_ap_load out integer,
out_cnt_pr_load out integer,
out_cnt_ap_not_load out integer,
out_cnt_pr_not_load out integer,
out_cnt_tot out integer,
out_message out varchar2
) as
file_date date;
ap_acct_no varchar2(16);
pr_acct_no varchar2(16);
-- ------------------------------------------------------
-- begin logic
-- ------------------------------------------------------
begin
file_date := to_date(in_file_date,'yyyymmdd');
out_cnt_tot := 0; --- THE ERROR IS ON THIS LINE ---
out_message := 'Test Message';
select brec_acct_code into ap_acct_no
from MSSU.zwkfi_bankrec_accts
where brec_acct_bank = in_bank_code
and brec_acct_type = 'AP';
select brec_acct_code into pr_acct_no
from MSSU.zwkfi_bankrec_accts
where brec_acct_bank = in_bank_code
and brec_acct_type = 'PR';
// The rest of the procedure...
Simple demo of the scenario mentioned in comments:
create or replace procedure p42(out_message out varchar2) as
begin
out_message := 'Test message';
end p42;
/
If I call that with a variable that is declared big enough, it's fine. I have a 12-char variable, so assigning a 12-char value is not a problem:
declare
msg varchar2(12);
begin
p42(msg);
end;
/
anonymous block completed
But if I make a mistake and make the caller's variable too small I get the error you're seeing:
declare
msg varchar2(10);
begin
p42(msg);
end;
/
Error report:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "STACKOVERFLOW.P42", line 3
ORA-06512: at line 4
06502. 00000 - "PL/SQL: numeric or value error%s"
*Cause:
*Action:
The error stack shows both the line in the procedure that errored (line 3), and the line in the caller that triggered it (line 4). Depending on where you're calling it you might not have the whole stack, of course.
You mentioned that there would be various error messagesin the future. You need to make sure that anything that ever calls this defines the variables to be big enough to cope with any of your messages. If they were stored in a table you could semi-automate that, otherwise it'll be a manual code review check.
OK, saw your c# comment after posting this. It looks like you're calling this constructor; that doesn't say what default size it gets, but not unreasonable to think it might be 1. So you need to call this constructor instead to specify the size explicitly:
OracleParameter(String, OracleType, Int32)
Initializes a new instance of the OracleParameter class that uses the parameter name,
data type, and length.
... something like:
OracleParameter prm15 = new OracleParameter("out_str_message",
OracleDbType.Varchar2, 80);
Unless there's a way to reset the size after creation, which I can't see. (Not something I've ever used!).