Strange error in PLSQL script - oracle

I have a strange error on this pl/sql script
'PL/SQL: numeric or value error: character to number conversion error'
in the is_prime function
if((nr mod it) = 0)
HERE->then
return cast(0 as int);
end if;
I can't figure out what I did wrong, because I pass only int's to is_prime and I have declared only a int in is_prime...
set serveroutput on;
drop table exresult;
create table exresult(AA int,BB int);
create or replace function is_prime(nr in int) return int is
it int := 0;
begin
for it in 2..floor(sqrt(nr)) loop
if((nr mod it) = 0)
then
return cast(0 as int);
end if;
end loop;
return cast(1 as int);
end is_prime;
/
create or replace function sum_digits(nr in int) return int is
summ int := 0;
tmp int := nr;
begin
loop
summ := summ + (tmp mod 10);
tmp := floor(tmp / 10);
exit when tmp = 0;
end loop;
return summ;
end sum_digits;
/
declare
target constant int := 5;
nmod int := 0;
nprm int := 0;
it int := 0;
begin
for it in 1..10000 loop
nmod := sum_digits(it) mod 10;
nprm := is_prime(it);
dbms_output.put_line(it + ',' + nmod + ',' + nprm);
if(nmod = target)
then
insert into exresult select it, nprm from dual;
end if;
end loop;
end;

Your problem is actually here:
dbms_output.put_line(it + ',' + nmod + ',' + nprm);
+ is not the concatenation operator in PL/SQL, you need ||:
dbms_output.put_line(it || ',' || nmod || ',' || nprm);
That's relying on implicit conversion of the int values to varchar2, which is fine, but more correctly should be:
dbms_output.put_line(to_char(it) || ',' || to_char(nmod) || ',' || to_char(nprm));

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

Is it possible to write function and procedure within single program in PL/SQL without using packages?

I'm trying to calculate LCM using GCF but somehow i'm getting error saying "no function with name LCM exists in the scope". What can i do about this?. I think this error is because i'm writing procedure and function together..
create or replace FUNCTION gcf (
x IN INTEGER,
y IN INTEGER
) RETURN INTEGER IS
res INTEGER;
BEGIN
IF ( y = 0 ) OR MOD(y,x) = 0 THEN
RETURN x;
ELSIF ( x = 0 ) THEN
RETURN y;
ELSIF ( x < y ) THEN
res := gcf(y,x);
ELSE
res := gcf(y,MOD(x,y) );
END IF;
RETURN res;
END;
/
create or replace PROCEDURE lcm (
num1 IN INTEGER,
num2 IN INTEGER,
answer OUT INTEGER
) IS
BEGIN
IF num1 = 0 AND num2 = 0 THEN
answer := 0;
ELSE
answer := abs(num1 * num2) / gcf(num1,num2);
END IF;
END lcm;
/
DECLARE
c integer;
BEGIN
dbms_output.put_line(' LCM(8, 12)-> ' || lcm(8, 12,c) );
dbms_output.put_line(' LCM(38,150)-> ' || lcm(38,150,c) );
dbms_output.put_line(' LCM(16,50)-> ' || lcm(16,60,c) );
dbms_output.put_line(' LCM(16,60)-> ' || lcm(16,60,c) );
dbms_output.put_line(' LCM(48,99)-> ' || lcm(48,99,c) );
END;
/
You can't use a PL/SQL procedure like a function. You must simply run it without being part of any other expression, which will set the value of the out parameter c.
DECLARE
c integer;
BEGIN
lcm(8, 12,c);
dbms_output.put_line(' LCM(8, 12)-> ' || c);
lcm(38,150,c);
dbms_output.put_line(' LCM(38,150)->' || c);
lcm(16,60,c);
dbms_output.put_line(' LCM(16,50)-> ' || c);
lcm(16,60,c);
dbms_output.put_line(' LCM(16,60)-> ' || c);
lcm(48,99,c);
dbms_output.put_line(' LCM(48,99)-> ' || c );
END;
/
Create the procedure and function inside the procedure as follows:
create or replace PROCEDURE lcm (
num1 IN INTEGER,
num2 IN INTEGER,
answer OUT INTEGER
) IS
FUNCTION gcf (
x IN INTEGER,
y IN INTEGER
) RETURN INTEGER IS
res INTEGER;
BEGIN
IF ( y = 0 ) OR MOD(y,x) = 0 THEN
RETURN x;
ELSIF ( x = 0 ) THEN
RETURN y;
ELSIF ( x < y ) THEN
res := gcf(y,x);
ELSE
res := gcf(y,MOD(x,y) );
END IF;
RETURN res;
END;
BEGIN
IF num1 = 0 AND num2 = 0 THEN
answer := 0;
ELSE
answer := abs(num1 * num2) / gcf(num1,num2);
END IF;
END lcm;
/
Then call it:
DECLARE
c INTEGER;
BEGIN
lcm (8, 12, c);
DBMS_OUTPUT.put_line (' LCM(8, 12)-> ' || c);
lcm (38, 150, c);
DBMS_OUTPUT.put_line (' LCM(38,150)-> ' || c);
lcm (16, 60, c);
DBMS_OUTPUT.put_line (' LCM(16,50)-> ' || c);
lcm (16, 60, c);
DBMS_OUTPUT.put_line (' LCM(16,60)-> ' || c);
lcm (48, 99, c);
DBMS_OUTPUT.put_line (' LCM(48,99)-> ' || c);
END;
The Lowest Common Multiple should be a function (since it returns a single value):
CREATE FUNCTION lcm (
num1 IN INTEGER,
num2 IN INTEGER,
) RETURN INTEGER DETERMINISTIC
IS
BEGIN
IF num1 = 0 AND num2 = 0 THEN
RETURN 0;
END IF;
RETURN abs(num1 * num2) / gcf(num1,num2);
END lcm;
/
Then you can use the code:
BEGIN
dbms_output.put_line(' LCM(8, 12)-> ' || lcm(8, 12) );
dbms_output.put_line(' LCM(38,150)-> ' || lcm(38,150) );
dbms_output.put_line(' LCM(16,50)-> ' || lcm(16,60) );
dbms_output.put_line(' LCM(16,60)-> ' || lcm(16,60) );
dbms_output.put_line(' LCM(48,99)-> ' || lcm(48,99) );
END;
/

Reading two dimensional pl/sql array

I am able to insert values but failed to retrieve values. Thanks in anticipation.
declare
type type1 is table of number;
type data_type is table of type1;
y data_type;
begin
y := data_type();
y.extend(100);
for i in 1..100 loop
y(i) := type1();
y(i).extend(100);
for j in 1..100 loop
y(i)(j) := i+j;
end loop;
end loop;
end;
If I understand well, you need a way to scan your arrays;
this could be a way:
declare
type type1 is table of number;
type data_type is table of type1;
y data_type;
k number := 2;
begin
y := data_type();
y.extend(k);
for i in 1..k loop
y(i) := type1();
y(i).extend(k);
for j in 1..k loop
y(i)(j) := i+j;
end loop;
end loop;
-- scanning
for i in y.first .. y.last loop
for j in y(i).first .. y(i).last loop
dbms_output.put_line('Y(' || i || ')(' || j || ') = ' || y(i)(j));
end loop;
end loop;
end;
the result:
Y(1)(1) = 2
Y(1)(2) = 3
Y(2)(1) = 3
Y(2)(2) = 4

ORACLE PL/SQL PIVOT procedure

I am trying to write an Oracle PL/SQL procedure to calculate a simple confusion matrix table. I have my labelled data prepared, basically two columns of 0 and 1, actual value vs predicted value.
I was able to calculate it with simple pivot (I think most straightforward option):
SELECT * FROM
( SELECT ACTUAL_VALUE, PREDICTED_VALUE
FROM MY_TABLE
)
PIVOT (
COUNT(PREDICTED_VALUE)
FOR PREDICTED_VALUE IN (1, 0))
ORDER BY ACTUAL_VALUE;
Now I am trying to "plug" all this into a DECLARE, BEGIN... framework but no success. Is it even possible to create procedure to calculate this pivot?
Thanks in advance for any suggestion!
I had to go with cursors and loops. And got my calculations working this way. So closing this one.Plus 3 more summing up calculations at the end.
Thank you
CREATE OR REPLACE PROCEDURE "mydb"."C_MATRIX"
IS
CURSOR MY_DATA IS
SELECT ACTUAL_CARD, PREDICTED_VALUE FROM my_table;
my_data_rec my_data%rowtype;
fp pls_integer := 0;
tp pls_integer := 0;
fn pls_integer := 0;
tn pls_integer := 0;
BEGIN
OPEN my_data;
LOOP
FETCH my_data INTO my_data_rec;
EXIT WHEN my_data%notfound;
IF my_data_rec.ACTUAL_CARD = 0 and my_data_rec.PREDICTED_VALUE = 0 then
tn := tn + 1;
elsif my_data_rec.ACTUAL_CARD = 0 and my_data_rec.PREDICTED_VALUE = 1 then
fn := fn + 1;
elsif my_data_rec.ACTUAL_CARD = 1 and my_data_rec.PREDICTED_VALUE = 1 then
tp := tp + 1;
elsif my_data_rec.ACTUAL_CARD = 1 and my_data_rec.PREDICTED_VALUE = 0 then
fp := fp + 1;
end if;
END LOOP;
dbms_output.put_line
('Number of false Positives: ' ||fp || ' Number of true Positives: ' ||tp || ' Total numbers of records: ' ||(fp + tp) );
dbms_output.put_line
('Number of false Negatives: ' ||fn || ' Number of true Negatives: ' ||tn || ' Total numbers of records: ' ||(fn + tn ));
dbms_output.put_line(' ');
dbms_output.put_line
(' Incorrect predictions: ' || (fp + fn) || ' Correct predictions: ' || (tp + tn) );
dbms_output.put_line(' ');
dbms_output.put_line(' Acurracy: ' || round((((tn + tp) / (tn + tp + fn + fp)) * 100),2) || '%');
dbms_output.put_line(' Precision: ' || round(((tp / (tp + fp)) * 100),2) || '%');
dbms_output.put_line(' Recall: ' || round(((tp / (tp + fn)) * 100),2) || '%');
END;
/

How write a PL/SQL program that prints out string which looking like xml format

Input String : “a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k3g5g5k3w2”
I tried this code as first step:
declare
word varchar2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num number := length(word)/2;
name_array dbms_sql.varchar2_table;
begin
dbms_output.put_line(word);
FOR i IN 1..num LOOP
name_array(i) := substr(word, -2*i, 2);
END LOOP;
FOR i IN name_array.FIRST .. name_array.LAST LOOP
dbms_output.put_line(name_array(i));
END LOOP;
end;
This code creates only an array of string. Not xml format. I need this output:
Which SQL functions,conditional clauses... do I need to use?
Oracle Setup:
CREATE OR REPLACE TYPE CHARS_TABLE IS TABLE OF CHAR(2);
/
CREATE OR REPLACE TYPE INTEGERS_TABLE IS TABLE OF INTEGER;
/
PL/SQL:
This assumes a well-formed set of character pairs and just indents each pair to the appropriate level:
DECLARE
word VARCHAR2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num PLS_INTEGER := LENGTH( word ) / 2;
name_array CHARS_TABLE := CHARS_TABLE();
depth_array INTEGERS_TABLE := INTEGERS_TABLE();
open_array INTEGERS_TABLE := INTEGERS_TABLE();
BEGIN
name_array.EXTEND( num );
depth_array.EXTEND( num );
open_array.EXTEND( num );
name_array(1) := SUBSTR( word, 1, 2 );
depth_array(1) := 1;
open_array(1) := 1;
FOR i IN 2 .. num LOOP
name_array(i) := SUBSTR( word, 2*i - 1, 2 );
open_array(i) := 1;
FOR j IN 1 .. i-1 LOOP
IF name_array(j) = name_array(i) THEN
open_array(i) := -open_array(i);
END IF;
END LOOP;
depth_array(i) := depth_array(i-1) + open_array(i);
END LOOP;
FOR i IN 1 .. num LOOP
FOR j IN 2 .. depth_array(i) + CASE open_array(i) WHEN 1 THEN 0 ELSE 1 END LOOP
DBMS_OUTPUT.PUT( ' ' );
END LOOP;
DBMS_OUTPUT.PUT_LINE( name_array(i) );
END LOOP;
END;
/
Output:
a4
b4
c2
d9
d9
c2
e6
e6
b4
s2
o1
o1
s2
a4
w2
r8
r8
k2
g5
g5
k2
w2
Update - Simpler Stack-Based Version:
DECLARE
word CONSTANT VARCHAR2(50) := 'a4b4c2d9d9c2e6e6b4s2o1o1s2a4w2r8r8k2g5g5k2w2';
num CONSTANT PLS_INTEGER := LENGTH( word ) / 2;
name_array CHARS_TABLE := CHARS_TABLE();
depth PLS_INTEGER := 0;
name CHAR(2);
PROCEDURE indent( depth PLS_INTEGER, name CHAR )
IS
BEGIN
FOR j IN 2 .. depth LOOP
DBMS_OUTPUT.PUT( ' ' );
END LOOP;
DBMS_OUTPUT.PUT_LINE( name );
END;
BEGIN
name_array.EXTEND( num );
FOR i IN 1 .. num LOOP
name := SUBSTR( word, 2*i - 1, 2 );
IF depth > 0 AND name = name_array(depth) THEN
indent(depth,name);
depth := depth - 1;
ELSE
depth := depth - 1;
name_array(depth) := name;
indent(depth,name);
END IF;
END LOOP;
END;
/
DECLARE
vs_CurrentChar VARCHAR2(1);
vs_NextChar VARCHAR2(1);
vs_TempText VARCHAR2(100);
vs_InputText VARCHAR2(100) := 'abcdffdcba';
vn_LengthOfText NUMBER := 1;
vn_WhileIndex NUMBER := 1;
vs_Spaces VARCHAR(100);
BEGIN
vs_TempText := NULL;
vs_CurrentChar := substr(vs_InputText, vn_WhileIndex, vn_LengthOfText);
dbms_output.put_line(vs_CurrentChar);
WHILE vn_WhileIndex < length(vs_InputText) - 1 LOOP
vs_NextChar := substr(vs_InputText, vn_WhileIndex + 1, vn_LengthOfText);
EXIT WHEN vs_CurrentChar = vs_NextChar;
vs_TempText := vs_TempText || vs_CurrentChar;
vs_CurrentChar := vs_NextChar;
vs_Spaces := NULL;
FOR i IN 1 .. vn_WhileIndex LOOP
vs_Spaces := vs_Spaces || chr(9); --'*';
END LOOP;
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
vn_WhileIndex := vn_WhileIndex + 1;
END LOOP;
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
FOR i IN 1 .. length(vs_TempText) LOOP
vs_Spaces := substr(vs_Spaces, vn_LengthOfText, length(vs_Spaces) - 1);
vs_CurrentChar := substr(vs_TempText, -i, vn_LengthOfText);
dbms_output.put_line(vs_Spaces || vs_CurrentChar);
END LOOP;
END;
/
And output:
a
b
c
d
f
f
d
c
b
a
even, if you put '*'; instead of chr(9); then output will look like as:
a
*b
**c
***d
****f
****f
***d
**c
*b
a

Resources