PlSQL showing compilation error - oracle

PL/SQL is showing compilation error. Function is working correctly and successfully compiled, but if running pl/sql query then showing compilation.
CREATE OR REPLACE FUNCTION f(
num IN NUMBER,
num2 IN NUMBER,
ans OUT NUMBER
) RETURN number IS
BEGIN
ans := num + num2;
RETURN ans;
END;
CREATE table add1(val1 number(2),val2 number(1),ans number(3));
SET SERVEROUTPUT ON ;
DECLARE
a1 add1.val1%TYPE;
b1 add1.val2%TYPE;
sum add1.ans%TYPE;
BEGIN
WHILE a1!=-99
LOOP
a1 := &a1;
b1 := &b1;
sum := f(a1, b1, sum);
INSERT INTO add1 VALUES(a1, b1, c1);
END LOOP;
END;

You cannot use IN and OUT in functions. Only in procedures. Correct approach is:
create or replace function f(
num number,
num2 number
) return number is
ans number;
begin
ans := num + num2;
return ans;
end;
UPD: Oh, boy! There are too many errors in your code. Maybe you should describe what you are trying to do with all that stuff.

Related

For oracle function to know if data is prime or not. Getting Warning: Function created with compilation errors

Wanted to create a function that can return records containing the number data type which are prime number
But getting warning of compilation error. What is the mistake in the code.I am a beginner in pl/sql.
CREATE OR REPLACE FUNCTION isPrime (num number)
RETURN number
IS
retVal number;
BEGIN
DECLARE
prime_or_notPrime number;
counter number;
retVal:= 1;
prime_or_notPrime:= 1
counter:= 2
WHILE (counter <= num/2) LOOP
IF (mod(num ,counter)= 0) THEN
prime_or_notPrime: = 0
EXIT;
END IF;
IF (prime_or_notPrime = 1 ) THEN
retVal: = 1;
counter: = counter + 1
END IF;
END LOOP;
return retVal;
END;
/
What is the mistake in the code
The DECLARE is invalid syntax for a function/procedure. You want to declare the variables between the IS and BEGIN keywords.
Then you are missing a ; statement terminator after prime_or_notPrime:= 1 and counter:= 2.
Then you have:
prime_or_notPrime: = 0 instead of prime_or_notPrime := 0;
retVal: = 1; instead of retVal := 1; and
counter: = counter + 1 instead of counter := counter + 1; (they all have a space between : and = and some are missing ; again).
You never set retVal to anything other than 1.
You do not handle NULL values or values lower than 2.
Fixing that (and the indentation) gives:
CREATE OR REPLACE FUNCTION isPrime (num number)
RETURN number
IS
retVal number;
prime_or_notPrime number;
counter number;
BEGIN
IF NUM IS NULL OR NUM < 2 THEN
RETURN 0;
END IF;
retVal:= 1;
prime_or_notPrime:= 1;
counter:= 2;
WHILE (counter <= num/2) LOOP
IF (mod(num ,counter)= 0) THEN
prime_or_notPrime := 0;
retVal := 0;
EXIT;
END IF;
IF (prime_or_notPrime = 1 ) THEN
counter := counter + 1;
END IF;
END LOOP;
return retVal;
END;
/
Note: the prime_or_notprime variable is not controlling anything as, as soon as you set it to 0 you EXIT from the loop so it will never be used again. Having noted that, you can get rid of the final IF statement and just always increment the counter.
You can simplify it to:
CREATE OR REPLACE FUNCTION isPrime (
num number
) RETURN number DETERMINISTIC
IS
BEGIN
IF NUM IS NULL OR NUM < 2 THEN
RETURN 0;
END IF;
FOR counter IN 2 .. SQRT(num) LOOP
IF MOD(num, counter) = 0 THEN
RETURN 0;
END IF;
END LOOP;
RETURN 1;
END;
/
You can then consider improvements. Such as starting by checking 2 as a special case and then skipping all the other even numbers.
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!!

plsql procedure is not a procedure or is undefined

I want to create a plsql procedure to calculate the factorial of a given number.
This is the procedure:
CREATE OR REPLACE PROCEDURE fact(x IN number, fact OUT number)
IS
BEGIN
while x > 0 loop
fact := x*fact;
x := x-1;
END loop;
END;
/
Warning: Procedure created with compilation errors.
and this is where I'm calling the function
DECLARE
x number := &x;
fact number := 1;
BEGIN
fact(x,fact);
dbms_output.put_line('Factorial is: '||fact);
END;
and this is the error I'm getting:
Enter value for x: 5
old 2: x number := &x;
new 2: x number := 5;
fact(x,fact);
*
ERROR at line 5:
ORA-06550: line 5, column 1:
PLS-00221: 'FACT' is not a procedure or is undefined
ORA-06550: line 5, column 1:
PL/SQL: Statement ignored
You need to convert your creation of procedure like this :
SQL> CREATE OR REPLACE PROCEDURE fact(x IN OUT number, fact OUT number) IS
BEGIN
while x > 0 loop
fact := x * nvl(fact, 1);
x := x - 1;
END loop;
END;
/
and call :
SQL> SET SERVEROUTPUT ON;
SQL> DECLARE
x number := &x;
v_fact number := 1;
BEGIN
fact(x, v_fact);
dbms_output.put_line('Factorial is: ' || v_fact);
END;
Factorial is: 120
You can not use a variable of type IN as an assignment target
You need to initialize a null variable fact as nvl(fact,1) or fact := 1; just before the while x > 0
Actually, you even do not need an extra parameter fact OUT number for procedure named fact, and make that as local. So, your procedure may be replaced with this :
SQL> CREATE OR REPLACE PROCEDURE fact(x IN OUT number) IS
fact number := 1;
BEGIN
while x > 0 loop
fact := x * fact;
x := x - 1;
end loop;
x := fact;
END;
/
Therefore, should be invoked as :
SQL> DECLARE
x number := &x;
BEGIN
fact(x);
dbms_output.put_line('Factorial is: ' || x);
END;
Factorial is: 120
You might consider rewriting this as a function along the lines of the following:
CREATE OR REPLACE FUNCTION fact(pinX IN INT)
RETURN INT
IS
nResult INT := 1;
BEGIN
FOR i IN 2..pinX LOOP
nResult := i * nResult;
END LOOP;
RETURN nResult;
END FACT;
dbfiddle here
Best of luck.

PL SQL - Lazy Caterer's Sequence

I need to write a PL/SQL program to print out the first 10 numbers of Lazy Caterer's sequence. I'm having trouble. I don't understand how to do this
Lazy caterer's sequence has the formula F(1)=2, F(n)=F(n-1)+n.
E.g., F(2) = F(1) + 2 = 2+2=4; F(3) = F(2)+3 = 4+3=7
Please use a loop. The numbers you print out should look like
2
4
7
...
56
DECLARE
n NUMBER := 1;
BEGIN
FOR i IN 1..10 LOOP
n := n + i;
DBMS_OUTPUT.PUT_LINE(n);
END LOOP;
END;
/
I cheated and looked up Lazy Caterer's algorithm on Wikipedia. This is an implementation of the simplistic version of the formula:
create or replace function lc_algo
(n in number)
return number as
begin
return (power(n,2) + n + 2) / 2;
end;
/
I admit it doesn't use a loop, but it's hard to see why anybody would need to. However, if your assignment insists on loops (because performance is not your bag) here you go:
create or replace function lc_algo
(n in number)
return number
as
rv simple_integer := 1;
begin
for i in 1..n loop
rv := rv + i;
end loop;
return rv;
end;
/
To get the first ten numbers (with whatever version):
select lc_algo(level) -- assuming you don't want to start with f(0)
from dual
connect by level <= 10
/

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