Lowest fraction Value In Oracle - oracle

I am trying to create a function to return lowest fraction value. the sample code is here :
create or replace function fraction_sh(x number) return varchar2
is
fra1 number;
pwr number;
intprt number;
v4 number;
numer number;
denom number;
gcdval number;
frac varchar2(50);
begin
if x <> 0 then
fra1 := mod(x,1);
pwr := length(mod(x,1))-1;
intprt := trunc(x);
numer :=mod(x,1)*power(10,length(mod(x,1))-1);
denom :=power(10,length(mod(x,1))-1);
gcdval := gcdnew(power(10,length(mod(x,1))-1),mod(x,1)*power(10,length(mod(x,1))-1));
if intprt = 0 then
frac := to_char(trunc(numer/gcdval))||'/'||to_char(trunc(denom/gcdval));
DBMS_OUTPUT.put_line(1||' '||denom||' '||gcdval||' '||numer);
else
frac := (intprt*to_char(trunc(denom/gcdval)))+to_char(trunc(numer/gcdval))||'/'||to_char(trunc(denom/gcdval));
DBMS_OUTPUT.put_line(2||' '||denom||' '||gcdval||' '||numer);
end if;
end if;
return frac;
end;
create or replace function gcdnew (a number, b number, p_precision number default null, orig_larger_num number default null) return number is
v_orig_larger_num number := greatest(nvl(orig_larger_num,-1),a,b);
v_precision_level number := p_precision;
begin
if a is null or b is null or (a = 0 and b = 0) then return 1; end if;
if p_precision is null or p_precision <= 0 then
v_precision_level := 4;
end if;
if b is null or b = 0 or (b/v_orig_larger_num <= power(10,-1*v_precision_level) and greatest(a,b) <> v_orig_larger_num) then
return a;
else
return (gcdnew(b,mod(a,b),v_precision_level,v_orig_larger_num));
end if;
end;
Inmost cases it works, but when i try to pass 2/11 it returns 2/10.
Any help appreciated.

The problem with what you're currently doing is precision. With 2/11 the resulting number is 0.1818181... recurring, and the length of that - and therefore the pwr value - end up as 40, which destroys the later calculations.
With modifications to limit the precision (and tidied up a bit, largely to remove repeated calculations when you have handy variables already):
create or replace function fraction_sh(p_float number) return varchar2
is
l_precision pls_integer := 10;
l_int_part pls_integer;
l_frac_part number;
l_power pls_integer;
l_numer number;
l_denom number;
l_gcdval number;
l_result varchar2(99);
begin
if p_float is null or p_float = 0 then
return null;
end if;
l_int_part := trunc(p_float);
l_frac_part := round(mod(p_float, 1), l_precision);
l_power := length(l_frac_part);
l_denom := power(10, l_power);
l_numer := l_frac_part * l_denom;
l_gcdval := gcdnew(l_denom, l_numer, ceil(l_precision/2));
if l_int_part = 0 then
l_result := trunc(l_numer/l_gcdval) ||'/'|| trunc(l_denom/l_gcdval);
else
l_result := l_int_part * (trunc(l_denom/l_gcdval) + trunc(l_numer/l_gcdval))
||'/'|| trunc(l_denom/l_gcdval);
end if;
return l_result;
end;
/
Which gets:
with t(n) as (
select 9/12 from dual
union all select 2/11 from dual
union all select 1/2 from dual
union all select 1/3 from dual
union all select 1/4 from dual
union all select 1/5 from dual
union all select 1/6 from dual
union all select 1/7 from dual
union all select 1/8 from dual
union all select 1/9 from dual
union all select 1/10 from dual
union all select 4/3 from dual
union all select 0 from dual
union all select 1 from dual
)
select n, fraction_sh(n) as fraction
from t;
N FRACTION
---------- ------------------------------
.75 3/4
.181818182 2/11
.5 1/2
.333333333 1/3
.25 1/4
.2 1/5
.166666667 1/6
.142857143 1/7
.125 1/8
.111111111 1/9
.1 1/10
1.33333333 4/3
0
1 1/1
So you might want to add some handling for either passing in 1, or the approximation after rounding ending up as 1/1 - presumably just to return a plain '1' in either case.
I've set l_precision to 10 rather arbitrarily, you can make that larger, but will hit problems at some point so test carefully with whatever value you pick.
(And I haven't looked at gdcnew at all; that can probably be simplified a bit too.)

you can use like this:
create or replace function fraction_sh(dividing number,divided number) return varchar2
is
dividing2 number;
divided2 number;
frac varchar2(100 char);
temp number;
loop_value boolean;
begin
loop_value:=true;
dividing2:=dividing;
divided2 :=divided;
if dividing <> 0 then
while loop_value
loop
if gcd(dividing2,divided2)<> 1 then
temp:=gcd(dividing2,divided2);
dividing2:=dividing2/temp;
divided2 :=divided2/temp;
frac:=dividing2||'/'||divided2;
else
loop_value:=false;
frac:=dividing2||'/'||divided2;
end if;
end loop;
else
frac:='0';
end if;
return frac;
end;
gcd func:
create or replace function gcd(a number, b number)
return number is
begin
if b = 0 then
return a;
else
return gcd(b,mod(a,b));
end if;
end;

Related

Write PL/SQL code to generate Armstrong number from 1 to 500

I am getting output as a = 1. I have taken a for loop from 1 to 500 and while loops inside the outer for loop.
declare
n number;
s number:=0;
r number;
len number;
m number;
begin
for a in 1..500 loop
m:=a;
n:=a;
len:=length(to_char(n));
while(n>0) loop
r:=mod(n,10);
s:=s+power(r,len);
n:=trunc(n/10);
end loop;
if m=s then
dbms_output.put_line('a='||to_char(a)');
end if;
end loop;
end;
How about this?
substr splits i to 3 separate digits
nvl is here to avoid adding null value if those digits don't exist (yet)
power function calculates i's cube
display i if it is equal to r (as "result")
SQL> declare
2 r number;
3 begin
4 for i in 1 .. 500 loop
5 r := power(to_number(substr(to_char(i), 1, 1)), 3) +
6 nvl(power(to_number(substr(to_char(i), 2, 1)), 3), 0) +
7 nvl(power(to_number(substr(to_char(i), 3, 1)), 3), 0);
8
9 if r = i then
10 dbms_output.put_line(i);
11 end if;
12 end loop;
13 end;
14 /
1
153
370
371
407
PL/SQL procedure successfully completed.
SQL>
You never reset s and you do not need the final IF statement (unless you are only interested in the Armstrong numbers which equal the original number):
declare
n number;
s number:=0;
r number;
len number;
m number;
begin
for a in 1..500 loop
m:=a;
n:=a;
s:=0; -- Reset s for each loop
len:=length(to_char(n));
while(n>0) loop
r:=mod(n,10);
s:=s+power(r,len);
n:=trunc(n/10);
end loop;
dbms_output.put_line(a || '=' || s); -- Output values for every loop.
end loop;
end;
/
db<>fiddle here

PL/SQL function returns minimal number

How to write a simple function that returns minimal salary. IN parameters are salary1,salary2,salary3 (9240, 9750, 8320) and it is forbidden to use finished functions.
I have no code whatsoever. I am very new at this and am trying to learn something.
The function is already exists in PLSQL. Check it out here. There are good examples of how to use it
UPD: As per Pavel mentioned here is a pair of examples
SQL:
select least(100, 1, 200) from dual; -- returns 1
PLSQL:
create or replace procedure get_min(s1 number, s2 number, s3 number)
is
min_sal number := 0;
begin
min_sal := least(s1, s2, s3);
dbms_output.put_line('min: ' || min_sal);
end;
if it is not allowed to use existing functions, you can do it using If then else
create or replace function get_min(s1 number, s2 number, s3 number)
return number
is
min_sal number := 0;
begin
---------------------
-- if the first value is less than the second value
-- , then the first value is the min of both values
-- otherwise the second value is min
if s1 < s2 then
min_sal := s1;
else
min_sal := s2;
end if;
------------------------------------
-- now you check the new min value against the third value.
if s3 < min_sal then
min_sal := s3;
end if;
dbms_output.put_line('min: ' || min_sal);
return min_sal;
end;
/
DECLARE
v_result NUMBER;
BEGIN
v_result := GET_MIN(1, 1, 1);
v_result := GET_MIN(2, 1, 1);
v_result := GET_MIN(1, 2, 1);
v_result := GET_MIN(1, 1, 2);
v_result := GET_MIN(2, 2, 1);
v_result := GET_MIN(1, 2, 2);
v_result := GET_MIN(1, 2, 3);
v_result := GET_MIN(2, 1, 3);
v_result := GET_MIN(3, 2, 1);
END;
/
Result:
dbms_output:
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
min: 1
db<>fiddle here

how to define multiple (WITH AS FUNCTIONS) in a single query?

trying to define multiple functions under a single with clause. which we normally do for CTE's. But for functions the same is not working. Please suggest solutions please.
With function dt ( b as number)
return number is
n number ;
begin
select 1 into n;
return n ;
end ;
dt2 ( c as number)
return number is
n1 number ;
begin
select 1 into n;
return n1;
end ;
select dt(1) , dt2(1) from dual
when using only dt i am able to get o/p but not with dt2.
It is working with the following example:
with
function x(p_NUM in number) return number
is
n number;
begin
SELECT 1 INTO N FROM DUAL;
--
return N;
--
end ;
--
function Y(p_NUM in number) return number
is
N1 NUMBER;
begin
SELECT 2 INTO N1 FROM DUAL;
--
return N1;
--
end ;
--
select X(1), Y(1)
from dual;
Cheers!!

Looping in PL/SQL variable is not a cursor

I'm trying to run the following loop:
DECLARE
v_banknumber varchar2(9) := '123456789';
v_counter number := 9;
v_result number;
begin
for i in v_banknumber
loop
v_result := v_counter * TO_NUMBER(i) + v_result;
v_counter := v_counter - 1;
end loop;
end;
I'm getting a error at line 2:
Error report -
ORA-06550: line 6, column 10:
PLS-00456: item 'V_BANKNUMBER' is not a cursor
ORA-06550: line 6, column 1:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
If I read this well, it seems like it should work. Anyone here that can explain me why it's not working?
The first digit must be multiplied by 9, the second with 8, the third with 7, and so on and save the sum of it in a result variable.
At a guess, what you want to do is
DECLARE
v_banknumber varchar2(9) := '123456789';
v_counter number := 9;
v_result number := 0;
begin
for i in 1..LENGTH(v_banknumber)
loop
v_result := v_counter * TO_NUMBER(SUBSTR(v_banknumber, i, 1)) + v_result;
v_counter := v_counter - 1;
end loop;
end;
This gives a result of 165.
Best of luck.
EDIT
Or you could really use a cursor:
DECLARE
v_banknumber varchar2(9) := '123456789';
v_counter number := 9;
v_result number := 0;
begin
for aRow in (SELECT LEVEL AS I FROM DUAL CONNECT BY LEVEL <= LENGTH(v_banknumber))
loop
v_result := v_counter * TO_NUMBER(SUBSTR(v_banknumber, aRow.I, 1)) + v_result;
v_counter := v_counter - 1;
end loop;
end;
Produces 165 as the result.
EDIT #2
Or, because there's no kill like overkill, you could just do it all in SQL:
WITH cteBank_number AS (SELECT '123456789' AS BANK_NUMBER FROM DUAL),
cteI AS (SELECT LEVEL AS I
FROM DUAL d
CROSS JOIN cteBank_number b
CONNECT BY LEVEL <= LENGTH(b.BANK_NUMBER)),
cteNums AS (SELECT TO_NUMBER(SUBSTR(b.BANK_NUMBER, LENGTH(b.BANK_NUMBER)-i.I+1, 1)) AS DIGIT,
i.I AS I,
TO_NUMBER(SUBSTR(b.BANK_NUMBER, LENGTH(b.BANK_NUMBER)-i.I+1, 1)) * i.I AS NUM
FROM cteBank_number b
CROSS JOIN cteI i)
SELECT SUM(NUM)
FROM cteNums n;
Still produces 165 as the result.
Your v_banknumber variable is a string not a cursor. You need to loop over each character in that string, and treat that character as a digit.
You could do this as:
set serveroutput on
declare
v_banknumber varchar2(9) := '123456789';
v_result number := 0;
begin
for v_counter in reverse 1..length(v_banknumber)
loop
v_result := v_result
+ (v_counter * to_number(substr(v_banknumber, -v_counter, 1)));
end loop;
dbms_output.put_line('The result is: ' || v_result);
end;
/
The result is: 165
PL/SQL procedure successfully completed.
With extra debugs to try to show what is happening on each iteration:
declare
v_banknumber varchar2(9) := '123456789';
v_result number := 0;
begin
dbms_output.put_line('length(v_banknumber) is: ' || length(v_banknumber));
for v_counter in reverse 1..length(v_banknumber)
loop
dbms_output.put_line('v_counter is: ' || v_counter);
dbms_output.put_line(' Digit is substr(v_banknumber, v_counter, 1): '
|| substr(v_banknumber, -v_counter, 1));
dbms_output.put_line(' Calculation for digit is: '
|| v_counter * to_number(substr(v_banknumber, -v_counter, 1)));
v_result := v_result
+ (v_counter * to_number(substr(v_banknumber, -v_counter, 1)));
dbms_output.put_line(' Running total: ' || v_result);
end loop;
dbms_output.put_line('The result is: ' || v_result);
end;
/
length(v_banknumber) is: 9
v_counter is: 9
Digit is substr(v_banknumber, v_counter, 1): 1
Calculation for digit is: 9
Running total: 9
v_counter is: 8
Digit is substr(v_banknumber, v_counter, 1): 2
Calculation for digit is: 16
Running total: 25
v_counter is: 7
Digit is substr(v_banknumber, v_counter, 1): 3
Calculation for digit is: 21
Running total: 46
v_counter is: 6
Digit is substr(v_banknumber, v_counter, 1): 4
Calculation for digit is: 24
Running total: 70
v_counter is: 5
Digit is substr(v_banknumber, v_counter, 1): 5
Calculation for digit is: 25
Running total: 95
v_counter is: 4
Digit is substr(v_banknumber, v_counter, 1): 6
Calculation for digit is: 24
Running total: 119
v_counter is: 3
Digit is substr(v_banknumber, v_counter, 1): 7
Calculation for digit is: 21
Running total: 140
v_counter is: 2
Digit is substr(v_banknumber, v_counter, 1): 8
Calculation for digit is: 16
Running total: 156
v_counter is: 1
Digit is substr(v_banknumber, v_counter, 1): 9
Calculation for digit is: 9
Running total: 165
The result is: 165
Did you mean to do it as an array?
DECLARE
type array_t is varray(9) of number;
a_banknumber array_t := array_t (1,2,3,4,5,6,7,8,9);
v_counter number := a_banknumber.count;
v_result number := 0;
begin
for i in 1..a_banknumber.count
loop
v_result := v_counter * a_banknumber(i) + v_result;
v_counter := v_counter - 1;
end loop;
dbms_output.put_line('Result: ' || v_result);
end;

factorial of a number in pl/sql

The following pl/sql program generates an error on execution on line the sum :=temp*sum; encountered symbol ; when expecting ( . Please explain my mistake.
declare
n number;
temp number;
sum number := 1;
begin
n := &n;
temp := n;
while temp>0 loop
sum := temp*sum;
temp := temp-1;
end loop;
dbms_output.put_line('Factorial of '||n||' is '||sum);
end;
/
Maybe not the answer to your question, but there is no need for PL/SQL here:
select round(exp(sum(ln(level))))
from dual
connect by level <= 5;
where 5 is your number (5!).
Additionally, if you like to operate faster in PL/SQL use pls_integer instead of number.
UPDATE
So according to comments I felt free to test:
create or replace package test_ is
function by_query(num number) return number deterministic;
function by_plsql(num number) return number deterministic;
end test_;
/
create or replace package body test_ is
function by_query(num number) return number deterministic
is
res number;
begin
select round(exp(sum(ln(level))))
into res
from dual
connect by level <= num;
return res;
end;
function by_plsql(num number) return number deterministic
is
n number := 0;
begin
for i in 1..num loop
n := n + ln(i);
end loop;
return round(exp(n));
end;
end test_;
So there are two functions with different content. Test query:
declare
dummy number;
begin
for i in 1..10000 loop
dummy := test_.by_query(5);
end loop;
end;
0.094 sec.
declare
dummy number;
begin
for i in 1..10000 loop
dummy := test_.by_plsql(5);
end loop;
end;
0.094 sec.
You'll say I am cheater and using deterministic keyword but here it is obvious and is needed by logic. If I remove it, the same scripts are working 1.7 sec vs 1.3 sec, so procedure is only a bit faster, there is no even double-win in performance. The totally opposite effect you will get if you use the function in a query so it is a fair trade.
Sum is reserved word in sql. Change variable name like
declare
n number;
temp number;
sum_ number := 1;
begin
n := &n;
temp := n;
while temp>0 loop
sum_ := temp*sum_;
temp := temp-1;
end loop;
dbms_output.put_line('Factorial of '||n||' is '||sum_);
end;
/
declare
n number;
i number;
sum_of_log_10s number;
exponent number;
base number;
begin
n := &n;
i := 1;
sum_of_log_10s := 0;
while i <= n loop
-- do stuff
sum_of_log_10s := sum_of_log_10s + log(10,i);
i := i + 1;
end loop;
dbms_output.put_line('sum of logs = '||sum_of_log_10s);
exponent := floor(sum_of_log_10s);
base := power(10,sum_of_log_10s - exponent);
dbms_output.put_line(n||'! = '||base||' x 10^'||exponent);
end;
I came up with this code that I like even better than #smnbbrv's answer. It's a great way to check the speed of a machine. I've been using a variation of this since my Atari 800
ALTER SESSION FORCE PARALLEL DDL PARALLEL 16;
ALTER SESSION FORCE PARALLEL DML PARALLEL 16;
ALTER SESSION FORCE PARALLEL QUERY PARALLEL 16;
with t as (
select /*+materialize*/
rownum i
from dual connect by rownum < 100000 -- put number to calculate n! here
)
,t1 as (
select /*+parallel(t,16)*/ /*+materialize*/
sum(log(10,i)) logsum
from t
)
select
trunc(power(10,(mod(logsum,1))),3) ||' x 10^'||trim(to_char(floor(logsum),'999,999,999,999')) factorial
-- logsum
from t1
;
-- returns 2.824 x 10^456,568
Here is the simple code for finding factorial of number at run time...
declare
-- it gives the final answer after computation
fac number :=1;
-- given number n
-- taking input from user
n number := &1;
-- start block
begin
-- start while loop
while n > 0 loop
-- multiple with n and decrease n's value
fac:=n*fac;
--dbms_output.put(n||'*');
n:=n-1;
end loop;
-- end loop
-- print result of fac
dbms_output.put_line(fac);
-- end the begin block
end;

Resources