How to create two variables refering the same nested table? - oracle

I expected that in the following code variables x and y refer the same nested table object but expression y = x seems to create empty table in y. So output is surprisingly 'No: a c' ( however I expected 'Yes: a b c'). What should I do in PL/SQL to have y referring the same nested table object as x.
declare
type str_t is table of varchar(100);
x str_t := str_t ();
y str_t ;
begin
x.extend; x(x.last) := 'a';
y := x;
y.extend; y(y.last) := 'b';
x.extend; x(x.last) := 'c';
dbms_output.put_line(case when x=y then 'Yes: ' else 'No: ' end);
for i in x.first .. x.last
loop
dbms_output.put_line(x(i));
end loop;
Сan it (passing nested table by reference - not by value) be written in PL/SQL or it's fundamentally impossible? Does any alternatives exist in the language to aviod the problem?

PL/SQL doesn't have references (but in one place we'll see later) so essentially what you're asking is impossible in PL/SQL. I don't see how that can be a problem, though.
In your example x and y are two different variables that are having the same type (a nested table collection), x := y is a variable assigment (deep copy), x = y is a comparison. See also e.g. Assigning Values to Variables.
I guess you've programming background with some other languages and you're trying to apply some other language paradigm here. You also might have an XY-problem.
One alternative is to use two different index variables to access the collection. That might or might not suit for your case:
declare
type str_list_t is table of varchar2(32767);
v_foos constant str_list_t := str_list_t('a', 'b', 'c');
-- have some business logic to get the right indices
i pls_integer := 1;
j pls_integer := 3;
begin
dbms_output.put_line(v_foos(i));
dbms_output.put_line(v_foos(j));
end;
/
But please note that changes to collection will invalidate the index variables (that's why I declared a constant collection).
When working with nested tables check also SQL multiset conditions that provide a powerful manipulations.
The only place with reference semantics is subprogram parameters where different parameter modes (IN, OUT or IN OUT) have different semantics (pass-by-value or pass-by-reference). But even there the decision is made by PL/SQL compiler/runtime - programmer has no much control.

So output is surprisingly 'No: a c' ( however I expected 'Yes: a b c').
Your expectation is wrong. Since the two arrays are having different values.
Let's see step by step:
x.extend;
x(x.last) := 'a';
So, x has value a.
y := x;
x is copied to y, so y has value a.
y.extend;
y(y.last) := 'b';
Now, y has a and b.
x.extend;
x(x.last) := 'c';
Now, x has a and c.
Finally,
X --> a,c
Y --> a,b
Let's see what your code shows the output:
SQL> set serveroutput on
SQL> DECLARE
2 type str_t
3 IS
4 TABLE OF VARCHAR(100);
5 x str_t := str_t ();
6 y str_t ;
7 BEGIN
8 x.extend;
9 x(x.last) := 'a';
10 y := x;
11 y.extend;
12 y(y.last) := 'b';
13 x.extend;
14 x(x.LAST) := 'c';
15 FOR i IN x.first .. x.last
16 LOOP
17 dbms_output.put_line('X'||'-'||x(i));
18 END LOOP;
19 FOR i IN y.first .. y.last
20 LOOP
21 dbms_output.put_line('Y'||'-'||y(i));
22 END LOOP;
23 END;
24 /
X-a
X-c
Y-a
Y-b
PL/SQL procedure successfully completed.
SQL>
So, X and Y are not equal.
To make them equal, set Y = X:
SQL> set serveroutput on
SQL> DECLARE
2 type str_t
3 IS
4 TABLE OF VARCHAR(100);
5 x str_t := str_t ();
6 y str_t ;
7 BEGIN
8 x.extend;
9 x(x.LAST) := 'a';
10 x.extend;
11 x(x.last) := 'b';
12 x.extend;
13 x(x.LAST) := 'c';
14 y := x;
15 dbms_output.put_line(case when x=y then 'Yes: ' else 'No: ' end);
16 FOR i IN x.first .. x.last
17 LOOP
18 dbms_output.put_line('X'||'-'||x(i));
19 END LOOP;
20 END;
21 /
Yes:
X-a
X-b
X-c
PL/SQL procedure successfully completed.
SQL>

Related

bind variable not declared error when using procedure in pl/sql

`
declare
x number:=&x;
y number:=&y;
m number;
s1 number;
a1 number;
create or replace procedure z(x in number,y in number,m out number,s1 out number,a1 out number)
is
begin
if x<y
then
m:=x;
else
m:=y;
end if;
s1:=a+b;
a1:s1/2;
end z;
/
begin
z(x,y,m,s1,a1);
dbms_output.put_line('Sum:'||sum1);
end;
/
/
I am trying to find the min sum and average of 2 numbers inputted by the user using pl/sql procedure
You can't use things you didn't declare. Also, it is good to check for syntax errors.
See comments:
SQL> create or replace procedure z(x in number,y in number,m out number,s1 out number,a1 out number)
2 is
3 begin
4 if x<y
5 then
6 m:=x;
7 else
8 m:=y;
9 end if;
10 s1:=x+y; --> x and y not a and b
11 a1:=s1/2; --> := not just :
12 end z;
13 /
Procedure created.
Testing:
SQL> declare
2 x number:=&x;
3 y number:=&y;
4 m number;
5 s1 number;
6 a1 number;
7 begin
8 z(x,y,m,s1,a1);
9 dbms_output.put_line('Sum:'||s1); --> s1 not sum1
10 end;
11 /
Enter value for x: 3
Enter value for y: 5
Sum:8
PL/SQL procedure successfully completed.
SQL>

what is wrong with the way I write procedures and functions in plsql?

1 declare
2 a number;
3 b number;
4 c number;
5 d number;
6 PROCEDURE findMin(x IN number, y IN number, z IN number , L out number) IS
7 BEGIN
8 IF x > y&& x>z then
9 L:= x;
10 ELSE if y>z&&y>x then
11 L:= y;
12 else
13 L:=z
14 END IF;
15 End if;
16 END;
17 BEGIN
18 a:= 23;
19 b:= 45;
20 c:=36;
21 findMin(a, b, c,d);
22 dbms_output.put_line(' Minimum of (23, 45,36) : ' || d);
END;
this is the first one I couldnt unerstand what is wrong with this code it is showing
*
ERROR at line 1:
ORA-06540: PL/SQL: compilation error
ORA-06553: PLS-906: Compilation is not possible
the second one is
2. DECLARE
3. num number;
4. c number;
5. PROCEDURE fact(x IN number, f out number) IS
6. BEGIN
7. IF x = 0 THEN
8. f:= x;
9. ELSE
10. f:= x*fact(x-1);
11. END IF;
12. END;
13. BEGIN
14. c:=f;
15. num:=6
16. fact(num,c);
17. dbms_output.put_line(' Factorial: ' ||'is'||c);
18. END;
19. /
i am getting output as
z:= x*fact(x-1) ;
*
ERROR at line 9:
ORA-06550: line 9, column 14:
PLS-00306: wrong number or types of arguments in call to 'FACT'
ORA-06550: line 9, column 8:
PL/SQL: Statement ignored
this is the second procedure i wrote but i couldnt get the problem in it
1 create or replace function tables(n in number) return number is s number;
2 begin
3 i number;
4 for i in 1...10 loop
5 s:=n*i;
6 end loop;
7 return s ;
8* end;
this is the multiplication table function what is wrong with my codes they are showing output as
Warning: Function created with compilation errors.
As of your 1st code: you should use AND, not && and terminate statements with a colon. When fixed, it runs (and produces wrong result, though, but I'll leave it to you):
SQL> DECLARE
2 a NUMBER;
3 b NUMBER;
4 c NUMBER;
5 d NUMBER;
6
7 PROCEDURE findMin (x IN NUMBER,
8 y IN NUMBER,
9 z IN NUMBER,
10 L OUT NUMBER)
11 IS
12 BEGIN
13 IF x > y
14 AND x > z
15 THEN
16 L := x;
17 ELSE
18 IF y > z
19 AND y > x
20 THEN
21 L := y;
22 ELSE
23 L := z;
24 END IF;
25 END IF;
26 END;
27 BEGIN
28 a := 23;
29 b := 45;
30 c := 36;
31 findMin (a,
32 b,
33 c,
34 d);
35 DBMS_OUTPUT.put_line (' Minimum of (23, 45,36) : ' || d);
36 END;
37 /
Minimum of (23, 45,36) : 45
PL/SQL procedure successfully completed.
SQL>
All that (37 lines of code) could be shortened to only one (which actually works):
SQL> select least(23, 45, 36) minimum from dual;
MINIMUM
----------
23
SQL>
As of your 2nd code: procedure is wrong as fact can't be used that way and lacks in 2nd parameter. One option to fix it is
SQL> DECLARE
2 num NUMBER;
3 c NUMBER;
4
5 PROCEDURE fact (x IN NUMBER, f OUT NUMBER)
6 IS
7 l_var NUMBER := 1;
8 BEGIN
9 FOR i IN 1 .. x
10 LOOP
11 l_var := l_var * i;
12 END LOOP;
13
14 f := l_var;
15 END;
16 BEGIN
17 num := 6;
18 fact (num, c);
19 DBMS_OUTPUT.put_line (' Factorial of ' || num || ' is ' || c);
20 END;
21 /
Factorial of 6 is 720
PL/SQL procedure successfully completed.
SQL>
Finally, the 3rd code: I have no idea what you meant to say with it, it's full of errors. I tried to salvage it, can't tell whether I succeeded.
SQL> CREATE OR REPLACE FUNCTION tables (n IN NUMBER)
2 RETURN NUMBER
3 IS
4 s NUMBER := 0;
5 BEGIN
6 FOR i IN 1 .. 10
7 LOOP
8 s := s + n * i;
9 END LOOP;
10
11 RETURN s;
12 END;
13 /
Function created.
SQL> SELECT tables (3) FROM DUAL;
TABLES(3)
----------
165
SQL>

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.

oracle show table like cartesian coordinate system

I have a table
create table test_table(
id number(10),
x number(10),
y number(10),
svalue number(10));
with filling a table as
declare
i integer;
begin
i := 0;
for x in 1 .. 10 loop
for y in 1 .. 10 loop
i := i + 1;
insert into test_table
(Id, x, y, svalue)
values
(i, x, y, x + y);
end loop;
end loop;
commit;
end;
how I can show table like
1 2 3 4 5 Ny
1 2 3 4 5 6
2 3 4 5 6 7
3 4 5 6 7 8
Nx
where x - rows, y - columns, svalue - value x,y
if we want to get pivot output for Cartesian coordinate system
run below script for create pivot function
http://paste.ubuntu.com/21378705/
pivot function is actually ref cursor that give dynamic column output after ruining above script run query as
select * from table(pivot('select x,y,svalue from test_table'))
in above query select statement use as following manner
select rowsection,columnsection,data from table
i hope this will help.
Hear i achieve cartesian coordinate system using looping test_table.
declare
HEAD VARCHAR2(100);
CURSOR c1 IS SELECT distinct x rec FROM test_table order by x;
CURSOR c2 IS SELECT distinct y rec FROM test_table order by y;
begin
-- for disply header in y
for y in c2 loop
HEAD := HEAD || lpad(y.rec,4);
end loop;
DBMS_OUTPUT.put_line( lpad('X',3) || HEAD );
--
-- disply value with repect to x and y
for x in c1 loop
declare
STR VARCHAR2(100);
CURSOR c2 IS SELECT svalue rec FROM test_table where x= x.rec order by y;
begin
for y in c2 loop
STR := str || lpad(y.rec,4);
end loop;
DBMS_OUTPUT.put_line(lpad(x.rec,3) || STR);
end;
end loop;
--
end;
i hope this will help.

Oracle CASE with OR

Can we use CASE statement in oracle with OR like this:
CASE WHEN A > 0 OR B >0 THEN c=1 END;
I know we can use AND, but I get an error when I use OR. Can you please suggest something? Thanks.
have you tried putting your OR statement in parens?
CASE WHEN (A > 0 OR B >0) THEN c=1 END;
You posted a CASE expression, but named it a CASE statement. That's probably where the confusion comes from. The CASE expression is valid:
SQL> declare
2 bool boolean;
3 a int := 1;
4 b int := 0;
5 c int := 1;
6 begin
7 bool := CASE WHEN A > 0 OR B >0 THEN c=1 END;
8 if bool is null
9 then
10 dbms_output.put_line('NULL');
11 elsif bool
12 then
13 dbms_output.put_line('TRUE');
14 else
15 dbms_output.put_line('FALSE');
16 end if;
17 end;
18 /
TRUE
PL/SQL procedure successfully completed.
But you probably meant to use the CASE-statement, which ends with "END CASE" instead of "END". An example:
SQL> declare
2 a int := 1;
3 b int := 0;
4 c int;
5 begin
6 case
7 when a > 0 or b > 0 then
8 c := 1;
9 end case
10 ;
11 dbms_output.put_line(c);
12 end;
13 /
1
PL/SQL procedure successfully completed.
Regards,
Rob.

Resources