Returning multiple values from an Oracle 12c function - sql

I am writing a function in Oracle 12c (and it has to be a function) that should return 3 values, order_line.o_id, a variable total that I create in the function, and a discounted_amount variable that I create in the function.
I have gotten my function to create the discounted_amount variable and return it but I am not sure how to get it to return these other two values.
CREATE OR replace FUNCTION DiscountsReport (input_o_id IN REAL)
RETURN REAL
IS
output_o_id NUMBER;
total REAL;
discounted_total REAL;
percent REAL;
BEGIN
output_o_id := input_o_id;
SELECT SUM(o.ol_quantity * o.ol_price)
INTO total
FROM order_line o
WHERE o.o_id = input_o_id;
IF total > 100 THEN
percent := 0.1;
ELSIF total > 200 THEN
percent := 0.2;
ELSE
percent := 0.0;
END IF;
discounted_total := ( total * ( 1 - percent ) );
RETURN discounted_total;
END;

Create a new type:
CREATE OR REPLACE TYPE new_type AS OBJECT(v1 type1, v2 type2, v3 type3);
and use it after RETURN (call the result output - its type is new_type).
You can access those values using:
output.v1
output.v2
output.v3

Related

exception in user defined functions and two select query in one user defined function

i actually only want to throw an exception if there is no rating for the tutor.
so i thought i would count how many ratings he has received so that i can also output how many ratings the tutor has received and if it is 0 or only 1 rating is, there will be thrown an exception that the average cannot be calculated.
I cant output both, it outputs the average in both lines
i also can't get the exception to throw out, is there maybe another method to do it
CREATE OR REPLACE FUNCTION averageRating (tutorrating NUMBER)
RETURN NUMBER
IS
countRating NUMBER;
averages NUMBER;
BEGIN
SELECT AVG(NumberStars) INTO averages
FROM Rating
WHERE Tutor_ID = tutorrating ;
SELECT count(customer_ID) INTO countRating
FROM Rating
WHERE Tutor_ID = tutorrating ;
Return averages;
Return countRating;
---EXCEPTION too few data
END averageRating;
DECLARE
averages number;
countRating NUMBER;
BEGIN
averages := averageRating(1);
countRating := averageRating(1);
dbms_output.put_line('The averageRating is:' || averages);
dbms_output.put_line('The number of ratings is:' || countRating);
END;
The output you want from your function, is not the expected behavior of the Oracle function. Though you can create one like below -
CREATE OR REPLACE FUNCTION averageRating (tutorrating IN NUMBER, -- By default parameter type is IN.
total_count_Rating OUT NUMBER) -- Since you need to return count_rating along with average and function can return only 1 value,
-- So you need 1 additional Out parameter for total_count_rating.
RETURN NUMBER
IS
countRating NUMBER;
averages NUMBER;
too_few_data EXCEPTION; -- Declared EXCEPTION for checking too_few_count.
BEGIN
SELECT AVG(NumberStars), count(customer_ID)
INTO averages, countRating
FROM Rating
WHERE Tutor_ID = tutorrating;
IF countRating <= 1 THEN
RAISE too_few_data;
END IF;
total_count_Rating := countRating;
Return averages;
EXCEPTION
WHEN too_few_data THEN
DBMS_OUTPUT.PUT_LINE('Rating is too few.');
total_count_Rating := 0; -- When there is too_few_Rating, Only then you nned to return total_count_Rating as 0.
RETURN 0; -- Return 0 will ensure to retun average as 0 when there is too_few_data to calculate the average.
END averageRating;
Then you can call your function as -
DECLARE
averages number;
countRating NUMBER;
BEGIN
averages := averageRating(1, countRating);
dbms_output.put_line('The averageRating is:' || averages);
dbms_output.put_line('The number of ratings is:' || countRating);
END;
Demo.

PL/SQL Script to create a procedure to calculate and return the area of a triangle

Hello guys i really need some help with oracle procedures and functions, I am a newbie in Oracle RDBMS and I can't seem to get the hang of it. Here is the question:
Define the Oracle PL/SQL script to create a procedure to calculate the area of a triangle:
1. Assuming it's a right angled triangle.
2. Assuming you have only been given the lengths of each if the 3 sides.
This one works, although I could not find how to embed a procedure to it:
declare
base integer;
height integer;
area integer;
BEGIN
height:= 12;
base := 10;
dbms_output.put_line('Height = '||height);
dbms_output.put_line('Base = '||base);
area := 0.5 * base * height;
dbms_output.put_line('The area of the triangle is ' ||area);
end;
Here's a function which uses Heron's formula:
create or replace function area_of_triangle
( a in number, b in number, c in number)
return number
is
s number;
t_area number;
begin
-- calculate semiperimeter
s := (a + b + c) / 2;
-- calculate area
t_area := sqrt(s * (s - a) * (s - b) * (s - c) );
return t_area;
end;
/

Find SUM and MAX in custom aggregate function

I've been working recently with custom aggregate function.
In this custom aggregate, the first function, doesn't compute the sum and the max value correctly.
I'm using a composite type to return the sum and the max value.
I've tried with appending everything with the array, but it's not an efficient way to work
CREATE TYPE sum_max_complex AS (sum real, max_v real);
CREATE OR REPLACE FUNCTION calculateSum(sum_max_complex, real) RETURNS sum_max_complex AS $$
DECLARE
sumValue real := 0;
max_v real := $2;
output sum_max_complex;
BEGIN
RAISE NOTICE '-------------------';
RAISE NOTICE 'IL PRIMO VALORE DI INPUT E: % ... %',$1.sum,$1.max_v;
RAISE NOTICE 'IL SECONDO VALORE DI INPUT E: %',$2;
IF $2 IS NOT NULL THEN
sumValue := calculateSumAggregate(sumValue,$2) + sumValue;
ELSE
sumValue := sumValue;
END IF;
max_v := searchmaximumvalue(max_v,$2);
output.sum := sumValue;
output.max_v := max_v;
RAISE NOTICE '-------------------';
RAISE NOTICE 'IL VALORE DI OUTPUT SONO: % ... %',output.sum,output.max_v;
RETURN output;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION addLaplacianNoiseSum(sum_max_complex) RETURNS real AS $$
DECLARE
epsilon real := 0.005;
sensivity real := $1.max_v;
laplaceDistribution real;
BEGIN
laplaceDistribution := generaterandomvalues(sensivity / (epsilon));
RETURN $1.sum + laplaceDistribution;
END;
$$ LANGUAGE plpgsql;
CREATE AGGREGATE SUM_LAPLACE(real)
(
SFUNC = calculateSum,
STYPE = sum_max_complex,
FINALFUNC = addLaplacianNoiseSum
);
In my table column, I have as values: 19,22,22.5,27.
It takes the correct value in the $2 parameter method, in the 1st function, but doesn't accumulate and sum every value.
It doesn't look like you are ever adding to the values stored in the sum_max_complex type. Here's a simplified example that shows approximately what you should do. I don't know what calculateSumAggregate or generaterandomvalues do, so I wasn't able to reproduce those.
CREATE TYPE sum_max_complex AS (sum real, max_v real);
CREATE OR REPLACE FUNCTION calculateSum(sum_max_complex, real) RETURNS sum_max_complex AS $$
select ROW(
$1.sum + coalesce($2, 0),
greatest($1.max_v, $2)
)::sum_max_complex;
$$ LANGUAGE SQL IMMUTABLE;
CREATE OR REPLACE FUNCTION addLaplacianNoiseSum(sum_max_complex) RETURNS real AS $$
select $1.sum + ($1.max_v/0.005);
$$ LANGUAGE SQL IMMUTABLE;
CREATE AGGREGATE SUM_LAPLACE(real)
(
SFUNC = calculateSum,
STYPE = sum_max_complex,
FINALFUNC = addLaplacianNoiseSum,
INITCOND = '(0, 0)'
);
with a as (select a from (values (19), (22), (22.5), (27)) v(a))
select sum_laplace(a) from a;
sum_laplace
-------------
5490.5

Mathematical operator for rounding the numbers up with lots of decimal places

I have a number: 0.01744649 and I need to round it from behind. I would like to get a result: 0.018
P.S.
I've tried all the possibilities of documentation: enter link description here - every time I get a different result but not this what I want.
Use ceil:
SELECT ceil(0.01744649 * 1000) / 1000
If you need to round one digit at a time, like this: 0.01744649 -> 0.0174465 -> 0.017447 -> 0.01745 -> 0.0175 -> 0.018, here's the function:
CREATE OR REPLACE FUNCTION public.rounding(_value numeric, _precision int)
RETURNS numeric AS
$BODY$
DECLARE
tmp_val numeric;
i integer;
BEGIN
tmp_val = _value;
i = 10;
WHILE i >= _precision LOOP
tmp_val = round(tmp_val, i);
i = i - 1;
END LOOP;
RETURN tmp_val;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Usage:
SELECT public.rounding(0.01744649, 3);
0.018
SELECT public.rounding(0.01744444, 3);
0.017
You just need to add 5/10000 before rounding to 3 decimals.
select round(0.01744649+0.0005,3);
round
-------
0.018
(1 row)
create or replace function dugi_round (
p_fl numeric,
p_pr int,
p_depth int default 0
) returns numeric language plpgsql as $$
declare n_fl numeric;
begin
n_fl := p_fl * 10.0;
-- raise notice 'we have now %, %',n_fl,p_pr;
if floor(n_fl) < n_fl then
-- raise notice 'remaining diff % - % = %',
-- n_fl, floor(n_fl), n_fl - floor(n_fl);
n_fl := dugi_round(n_fl, p_pr, p_depth + 1);
end if;
if (p_depth > p_pr) then
n_fl := round(n_fl / 10);
else
n_fl := round(n_fl / 10, p_pr);
end if;
-- raise notice 'returning %, %', n_fl, p_pr;
return n_fl;
end;
$$
;
ghp=# select dugi_round(0.01744649, 3);
dugi_round
------------
0.018
(1 row)
If you're trying to round it to the 3rd decimal, try to multiply it by 1000, ceil it and divide it by 1000 again. That should produce the result you're expecting.

Trying to Understand Oracle Deterministic Function Speed Test Results

This question arose as a spin-off from this one: Adding Many (UDFs) Validation Functions to Oracle - Which Method Run Fastest
I am debating between putting a low level function that will be used everywhere in my application in an object as a CONSTRUCTOR FUNCTION so it's encapsulated with it's type that it returns, or just making the function STAND-ALONE. I know most would thing these test result are inconsequential because the loop count is so high. But in our app this function could be used to check multiple columns in each large loop iteration. So it really wouldn't be hard for us to reach 3 million checks in a single process. And this all reports back to a web page so the user is waiting on these results, so every msec matters.
So I was running some results and this is what I found...
/*****
isValid is a CONSTRUCTOR FUNCTION of an OBJECT
and it is deterministic
passing in:
'blah' -> 12 seconds
x -> 464 msecs
changing x to varchar2(7)
x -> 2 seconds (this is how we would use it in most cases within loops)
*****/
declare
x number;
--x varchar2(7);
begin
for i in 1 .. 3000000 loop
x := x + 1;
if (isValid(x,'number').result = 1) then
null;
end if;
end loop;
end;
/
/*****
isValid2 is a STAND-ALONE FUNCTION
and it is deterministic
passing in:
'blah' -> 407 msecs
x -> 4 seconds
changing x to varchar2(7)
x -> 4 seconds (this is how we would use it in most cases within loops)
*****/
declare
x number;
--x varchar2(7);
begin
for i in 1 .. 3000000 loop
x := x + 1;
if (isValid2(x,'number').result = 1) then
null;
end if;
end loop;
end;
/
Based on these result I think I'm going to go with the CONSTRUCTOR FUNCTION of an OBJECT approach.
So my question is: Why are the result between 'blah' and x completely reversed between the two different methods?
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
PL/SQL Release 11.2.0.2.0 - Production
CORE 11.2.0.2.0 Production
TNS for 64-bit Windows: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production
PLSQL_OPTIMIZE_LEVEL = 2 (I've never read up on this, this is a test box, I'm not sure what it *should* be set to yet but I will read up on it)
PLSQL_CODE_TYPE = INTERPRETED (I know NATIVE is faster but they won't change it.)
Here is the stand-alone function...
create or replace type valObj as object (
result number(1),
resulttext varchar2(32000) );
/
create or replace function isValid2(v in varchar2, f in varchar2)
return valObj
deterministic
is
test PLS_INTEGER;
begin
if f = '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 f = 'phone' then
null; --TO DO
elsif f = 'integer' then
null; --TO DO
elsif f = 'email' then
null; --TO DO
elsif f = 'IPv4' then
null; --TO DO
elsif f = 'IPv6' then
null; --TO DO
end if;
--dozens of others to follow....
end;
/
And here is the object/function...
create or replace type isValid as object (
result number(1),
resulttext varchar2(32000),
constructor function isValid(v varchar, f varchar) return self as result deterministic );
/
create or replace type body isValid as
constructor function isValid(v varchar, f varchar) return self as result deterministic is
test PLS_INTEGER;
begin
if f = '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 f = 'phone' then
null; --TO DO
elsif f = 'integer' then
null; --TO DO
elsif f = 'email' then
null; --TO DO
elsif f = 'IPv4' then
null; --TO DO
elsif f = 'IPv6' then
null; --TO DO
end if;
--dozens of others to follow....
end;
end;
/