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

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

Related

PLSQL (finding range of prime number to 1000 )

DECLARE
n NUMBER;<br>i NUMBER;
pr NUMBER;
BEGIN
FOR n IN 2 .. 1000 LOOP
pr := 1;
FOR i IN 2 .. n / 2 LOOP
IF MOD(n, i) = 0 THEN
pr := 0;
END IF;
END LOOP;
IF (N = 997) THEN
DBMS_OUTPUT.PUT(n);
pr:=2;
ELSE
IF pr = 1 THEN
DBMS_OUTPUT.PUT(n||'&');
END IF;
END if;
END LOOP;
dbms_output.new_line;
END;
output should be as in one line----> 2&3&5&7&11&13&17&19&23&29&31&37&41&43&47&53&59&61&67&71&73&79&83&89&97&101&103&107&109&113&127&131&137&139&149&151&157&163&167&173&179&181&191&193&197&199&211&223&227&229&233&239&241&251&257&263&269&271&277&281&283&293&307&311&313&317&331&337&347&349&353&359&367&373&379&383&389&397&401&409&419&421&431&433&439&443&449&457&461&463&467&479&487&491&499&503&509&521&523&541&547&557&563&569&571&577&587&593&599&601&607&613&617&619&631&641&643&647&653&659&661&673&677&683&691&701&709&719&727&733&739&743&751&757&761&769&773&787&797&809&811&821&823&827&829&839&853&857&859&863&877&881&883&887&907&911&919&929&937&941&947&953&967&971&977&983&991&997
but not working in hackerrank compiler question is "Print Prime Numbers"
There's no problem in your code to produce the desired string except <br>i NUMBER;
Just comment out that piece in the declaration section as below :
DECLARE
n NUMBER; -- <br>i NUMBER;
pr NUMBER;
Here's a Demo for it.

PLSQL - How does this Prime number code work?

DECLARE
i number(3);
j number(3);
BEGIN
i := 2;
LOOP
j:= 2;
LOOP
exit WHEN ((mod(i, j) = 0) or (j = i));
j := j +1;
END LOOP;
IF (j = i ) THEN
dbms_output.put_line(i || ' is prime');
END IF;
i := i + 1;
exit WHEN i = 50;
END LOOP;
END;
The code works properly. I tried to figure out how it works and ended up having 4 as a prime number, which isn't. If you could help me understand how this nested loop works, I'd be very thankful.
Thank you.
The code is looking for all the prime numbers up to 50. The outer loop is just checking each value of i from 2 to 50 to see if that integer is prime.
For each value of i, it tries to divide that integer by every other integer one by one, starting from 2. If i is divisible by j with no remainder (mod is zero) then it is not prime; unless it is only divisible by itself (j=1).
It exits that inner loop as soon as it finds a value of j which divides into i, or it reaches i itself.
It then needs a further check to see which of those conditions actually caused it to exit; and thus whether or not it is actually prime.
You could do the same thing with slightly clearer (IMHO) logic:
BEGIN
<<OUTER>>
FOR i IN 2..50 LOOP
FOR j IN 2..i-1 LOOP
IF (mod(i, j) = 0) THEN
CONTINUE OUTER;
END IF;
END LOOP;
dbms_output.put_line(i || ' is prime');
END LOOP;
END;
/
Lets rewrite it so its a bit simpler:
BEGIN
<<outer_loop>>
FOR value IN 2 .. 50 LOOP
FOR divisor IN 2 .. value - 1 LOOP
CONTINUE outer_loop WHEN MOD( value, divisor ) = 0;
END LOOP;
DBMS_OUTPUT.PUT_LINE( value || ' is prime' );
END LOOP;
END;
/
All it is doing is, in the outer loop going through the number 2 .. 50 and in the inner loop is checking whether there is a number that divides exactly into that value; if there is then continue the outer loop and if there is not then output that the number is prime.
Your code is effectively the same code but it is complicated by not using FOR .. IN .. loops
If I understand your question.
When i = 4 and j = 2 then condition ((mod(i, j) = 0) or (j = i)) leads to the exit from inner loop, but condition (j = i ) is false and program doesn't go to line dbms_output.put_line(i || ' is prime');

For loop and its reverse based on if-condition/ternary operation in Oracle

In Oracle, I have the following almost identical SQL in an if-else block of a stored procedure:
if v_endsid = '15' then
FOR i IN 1..v_num LOOP --loop around the number from the beginning
v_item := TRIM(SUBSTR(v_str, (i - 1) * 12 + 1, 12)); --now this is the evaluated item
if v_item = TRIM(cursorElement.ITEM) then --this is the time to break
break;
end if;
END LOOP;
else
FOR i IN REVERSE 1..v_num LOOP --loop around the number from the last
v_item := TRIM(SUBSTR(v_str, (i - 1) * 12 + 1, 12)); --now this is the evaluated item
if v_item = TRIM(cursorElement.ITEM) then --this is the time to break
break;
end if;
END LOOP;
end if;
As you can see, the only difference between the SQL in the if and else blocks is the FOR loop. One is a forward for loop, another is a reversed (backward) for loop.
Is there any way to combine the block? I am trying this:
FOR i IN (case when v_endsid = '15' then 1..v_num else REVERSE 1..v_num end) LOOP
v_item := TRIM(SUBSTR(v_str, (i - 1) * 12 + 1, 12)); --now this is the evaluated item
if v_item = TRIM(cursorElement.ITEM) then --this is the time to break
break;
end if;
END LOOP;
But it gives me compilation error in the 1..v_num:
Found: '..' Expecting: END -or- ELSE -or- WHEN -or- OR -or- AND -or-
BETWEEN IN LIKE LIKE2 LIKE4 LIKEC MEMBER SUBMULTISET -or- ! != < <= <>
= > >= ^ ^= IS NOT ~
There is no way of dynamically changing the direction of the for loop. The only thing you can do here if you want to combine the two blocks is to use a basic loop
if v_endsid = '15' then
i := 1;
reverse := false;
else
i := v_num;
reverse := true;
end if;
LOOP --loop around the number from the beginning
v_item := TRIM(SUBSTR(v_str, (v_num - 1) * 12 + 1, 12)); --now this is the evaluated item
if v_item = TRIM(cursorElement.ITEM) then --this is the time to break
break;
end if;
if reverse = true then
if i = 1 then
exit;
else
i := i - 1;
else
if i = v_num then
exit;
else
i := i + 1;
end if;
end if;
END LOOP;
The final solution I adapt is by using basic LOOP and some ternary operations:
i := case when v_endsid = '15' then 1 else v_num end; --evaluates from front or back depending on the case
Loop
v_item := TRIM(SUBSTR(v_str, (i - 1) * 12 + 1, 12)); --now this is the evaluated item
--other queries
i := i + (case when v_endsid = '15' then 1 else -1 end);
exit when i = 0 or i = v_num + 1; --exceeds the elements
end loop;
This, I think, is a fairly neat working replacement for the original SQL
How about the another way round ? Instead of manipulating the loop condition encapsulate the code evaluating the break condition into a function that can be called from the different loops.
declare
v_reverse constant boolean := true;
-- your parameters and break rule can be arbitrary complex, mine is simple
-- as this is just a demonstration
function break(i in pls_integer) return boolean is
begin
return 13 = i;
end;
begin
if v_reverse
then
for i in reverse 1 .. 15
loop
dbms_output.put_line(i);
exit when break(i);
end loop;
else
for i in 1 .. 15
loop
dbms_output.put_line(i);
exit when break(i);
end loop;
end if;
end;
/

PL/SQL Check Digit, luhn using MOD 11

So here is the question:
Write code to take in an id and determine if the check digit is correct
UPDATED CODE:
Set SERVEROUTPUT ON
DECLARE
val_num NUMBER := '&user_input';
holder NUMBER := 0;
y NUMBER := 0;
conv_string VARCHAR2(20);
BEGIN
conv_string := to_char(val_num*10);
for x in 1..length(conv_string) loop
y := to_number(substr(conv_string, -x, 1));
if mod(x,2) = 0 then
y := y * 2;
if y > 9 then
y := y - 9;
end if;
end if;
holder := holder + y;
end loop;
dbms_output.put_line ('Check is '||(11-Mod(holder, 11)));
END luhn;
/
SET SERVEROUTPUT ON
The return is:
SQL> # loop
Enter value for user_input: 036532
old 2: val_num NUMBER := '&user_input';
new 2: val_num NUMBER := '036532';
Check is 2
It should be 6
Before actual execution
SET SERVEROUTPUT ON
to enable SQL*Plus to fetch database output buffer.
Here is solution: https://community.oracle.com/thread/837639?start=0&tstart=0
There are lots of different variations of the luhn algorithm, so looking at these implementations and your (I think incomplete) description in the comments I think this may be fairly close to what you are looking for, and gives the correct checksum for 036532 as per your initial question.
Hope it is helpfull
Set SERVEROUTPUT ON
DECLARE
val_num number := '036532';
holder NUMBER := 0;
y NUMBER := 0;
conv_string VARCHAR2(20);
BEGIN
conv_string := to_char(val_num);
FOR X IN 1..LENGTH(CONV_STRING) LOOP
Y := TO_NUMBER(SUBSTR(CONV_STRING, -X, 1));
IF ((X+1) > 10) THEN
Y := Y * 10;
ELSE
Y := Y * (X + 1);
END IF;
IF (Y >= 10) THEN
HOLDER := HOLDER + TO_NUMBER(substr(TO_CHAR(Y), 1, 1)) + TO_NUMBER(substr(TO_CHAR(Y), 2, 1));
ELSE
HOLDER := HOLDER + Y;
END IF;
END LOOP;
HOLDER := MOD(HOLDER, 11);
Holder := 11 - mod(holder, 11);
dbms_output.put_line ('Check is '|| holder);
END luhn;
/
SET SERVEROUTPUT ON

PL SQL wrong output

I am trying to write a code which checks whether a number is binary or not.
Here is my code.
declare
n INTEGER:=&num;
ch number:=0;
begin
loop
exit when n=0;
if(mod(n,10)!=0 or mod(n,10)!=1) then
ch:=1;
exit;
end if;
n:=n/10;
end loop;
if ch=1 then
dbms_output.put_line('Not a Binary number.');
else
dbms_output.put_line('Binary!!!');
end if;
end;
/
I am using Oracle 11g SQL Plus.
Sometimes it is giving error at line 2. Here is the snippet of error.
old 2: n INTEGER:=&num;
new 2: n INTEGER:=;
n INTEGER:=;
*
ERROR at line 2:
ORA-06550: line 2, column 12:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
( - + case mod new not null <an identifier>
And if it is running correctly then for every number it is giving the same output as 'Not a Binary number.'.
Change the OR by AND in the IF statement. It seems to do what you want.
DECLARE
n INTEGER := &num;
ch NUMBER := 0;
BEGIN
LOOP
EXIT WHEN n = 0;
IF MOD(n, 10) != 0
AND MOD(n, 10) != 1
THEN
ch := 1;
EXIT;
END IF;
n := n / 10;
END LOOP;
IF ch = 1
THEN
dbms_output.put_line('Not a Binary number.');
ELSE
dbms_output.put_line('Binary!!!');
END IF;
END;
/

Resources