Updating variables in for loop with PL/SQL - oracle

Basically the code is supposed to run this function for 10 iterations: F(n) = 5 * F(n-1) - 4 * F(n-2) where F(0)=2 and F(1)=5. F(2) should be 17 and F(3) should be 65.
Problem is, when I'm running this, the variables don't seem to update so I'm stuck with it repeating with first three variables correctly (2, 5, and 17) and then 17 as well for the rest of the results. (Screenshot of the exact results below the code). Where did I go wrong?
declare
justn int;
nminusone int;
nminustwo int;
begin
--action;
nminusone := 5;
nminustwo := 2;
dbms_output.put_line('F(0) is: ' || nminustwo);
dbms_output.put_line('F(1) is: ' || nminusone);
for i in 2..10 loop
justn := 5*nminusone-4*nminustwo;
nminusone := justn;
nminustwo := nminusone;
dbms_output.put_line('F(' || i || ') is: ' || justn);
end loop;
end;
Screenshot of my results

Change the order of assignments. Current order makes nminustwo and nminusone equal.
..
nminustwo := nminusone;
nminusone := justn;
..

Related

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
/

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;

increment function in plsql varchar2

I want use this in a varchar2. Ex.:
declare
num number := &Number;
serie varchar2(200) := 'S = ';
begin
for x in 1 .. num loop
serie += x, ' + ';
end loop; `
end;
/
In the end I want that the serie be like "S = 1 + 2 + 3 ..." How can i make that work?
That would be something like this:
SQL> set serveroutput on
SQL> declare
2 num number := &Number;
3 serie varchar2(200) := 'S = ';
4 begin
5 for x in 1 .. num loop
6 serie := serie || to_char(x) || ' + ';
7 end loop;
8
9 -- remove the trailing "+"
10 serie := rtrim(serie, ' +');
11 dbms_output.put_Line(serie);
12 end;
13 /
Enter value for number: 5
S = 1 + 2 + 3 + 4 + 5
PL/SQL procedure successfully completed.
SQL>
A few comments:
line 6: you have to concatenate (concatenation operator is a double pipe sign, ||) previous value of SERIE; otherwise, you'd have only the last number in it
line 10: remove the trailing "+" sign
What could be done in pure SQL, usually should be done in pure SQL:
declare
num number := &Number;
serie varchar2(200);
begin
select 'S = ' || listagg(rownum, ', ') within group (order by rownum)
into serie
from dual
connect by level <= num;
dbms_output.put_line(serie);
end;
/
The result for num = 10:
S = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Note, listagg function was introduced in Oracle version 11.2.

The First 8 Square Numbers by using loop in Oracle?

How to compute The First 8 Square Numbers by using loop in Oracle?
declare
total integer;
i integer;
begin
total := 0;
i := 1;
loop
total := total *i;
i := i*total;
exit when i > 8;
end loop;
dbms_output.put_line('the total is ' || total); end;
Please examine carefully this fragment:
i := 1;
loop
total := total *i;
i := i*total;
exit when i > 8;
end loop;
There is exit when i > 8; command that is supposed to exit the loop when i is greater than 8, but i is always 1 within the loop, so the loop is infinite and you never get any result.
You must increment i somwhere in the loop using i := i + 1; instruction.
This is not for your homework, but provides a solution for the question.
SELECT LEVEL * LEVEL squares
FROM DUAL
CONNECT BY LEVEL <= 8;
Output:
1
4
9
16
25
36
49
64

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