So I know I can initialize variables in PL/SQL using either of the following:
The DEFAULT keyword
The := assignment operator
For example:
counter binary_integer DEFAULT 15;
counter binary_integer := 15;
Are these two methods exactly equivalent to the PL/SQL engine, or are there any slight differences?
Yes they are equivalent.
From Oracle documentation
You can use the keyword DEFAULT instead of the assignment operator to
initialize variables. You can also use DEFAULT to initialize
subprogram parameters, cursor parameters, and fields in a user-defined
record.
Use DEFAULT for variables that have a typical value. Use the
assignment operator for variables (such as counters and accumulators)
that have no typical value.
Example 2-8 Assigning Default Values to Variables with DEFAULT Keyword
SQL> DECLARE
2 blood_type CHAR DEFAULT 'O'; -- Same as blood_type CHAR := 'O';
3
4 hours_worked INTEGER DEFAULT 40; -- Typical value
5 employee_count INTEGER := 0; -- No typical value
6
7 BEGIN
8 NULL;
9 END;
10 /
PL/SQL procedure successfully completed.
SQL>
According to the documentation you can use either:
To specify the initial value, use either the assignment operator (:=) or the keyword DEFAULT, followed by an expression
which means they are equivalent.
Related
CREATE TYPE o_A AS OBJECT (A1 VARCHAR2 (4000));
CREATE OR REPLACE TYPE t_A IS TABLE OF o_A;
CREATE TABLE table_o_A
(
oA_id INTEGER GENERATED BY DEFAULT AS IDENTITY,
oA o_A,
PRIMARY KEY (oA_id)
);
I would to add the lines of a t_A in the table_oA.
CREATE PROCEDURE aa (query_result t_A)
is
v_i INTEGER;
begin
SELECT MAX (oA_id)
INTO v_i
FROM table_o_A;
FORALL i IN INDICES OF query_result
INSERT INTO table_o_A(
oA_id,
oA)
VALUES ( v_i+i, query_result (i));
end;
[Error] Compilation (887: 26): PLS-00430: FORALL iteration variable I is not allowed in this context
I thinks it doesn't work because i isn't an integer.
I could use a for loop and use a real integer but it make a switch between the pl/sql and sql context.
How do I do that with the ForAll loop?
code
solution from Alex Poole
I thinks it doesn't work because i isn't an integer.
No, this is a restriction on the forall syntax; from the documentation, your i index is (emphasis added):
Name for the implicitly declared integer variable that is local to the FORALL statement. Statements outside the FORALL statement cannot reference index. Statements inside the FORALL statement can reference index as an index variable, but cannot use it in expressions or change its value. After the FORALL statement runs, index is undefined.
Your oA_id is a sequence-backed identity column - albeit only default, not always - so you don't have to specify a value. Unless you're already mixing manual and auto-assigned ID values, the max you get will be compatible with the sequence value (there may be gaps, of course); but having set ID values manually, the sequence will then be out of step and you'd get clashes from auto-assigned values, unless it was reset to the current limit.
Unless you have a good reason to mix manual and auto, you can simplify what you are doing to just:
CREATE PROCEDURE a (query_result t_A)
is
begin
FORALL i IN INDICES OF query_result
INSERT INTO table_o_A(oA)
VALUES (query_result (i));
end;
/
... and only provide the objects in the insert statement.
db<>fiddle
I read about IN OUT and NOCOPY. Then I encountered NOCOPY use cases but I was not able to get it. Can anybody explain these with examples? Thanks in advance.
The actual parameter must be implicitly converted to the data type of the formal parameter.
The actual parameter is the element of a collection.
The actual parameter is a scalar variable with the NOT NULL constraint.
The actual parameter is a scalar numeric variable with a range, size, scale, or precision constraint.
The actual and formal parameters are records, one or both was declared with %ROWTYPE or %TYPE, and constraints on corresponding fields differ.
The actual and formal parameters are records, the actual parameter was declared (implicitly) as the index of a cursor FOR LOOP statement, and constraints on corresponding fields differ.
The subprogram is invoked through a database link or as an external subprogram.
The basic principle is that PL/SQL will honour the NOCOPY directive as long as the value we pass can be used as provided, without transformation, and is addressable by the called program. The scenarios you list are circumstances where this is not the cases. I must admit a couple of these examples made me think, so this is a worthwhile exercise.
The first four examples call this toy procedure.
create or replace procedure tst2 (p1 in out nocopy t34%rowtype) is
begin
p1.id := 42;
end;
/
Case 1: The actual parameter must be implicitly converted to the data type of the formal parameter.
declare
n varchar2(3) := '23';
begin
tst(n);
dbms_output.put_line(n);
end;
/
Case 2: The actual parameter is the element of a collection.
declare
nt sys.odcinumberlist := sys.odcinumberlist(17,23,69);
begin
tst(nt(2));
dbms_output.put_line(to_char(nt(2)));
end;
/
Case 3: The actual parameter is a scalar variable with the NOT NULL constraint.
declare
n number not null := 23;
begin
tst(n);
dbms_output.put_line(to_char(n));
end;
/
Case 4: The actual parameter is a scalar numeric variable with a range, size, scale, or precision constraint.
declare
n number(5,2) := 23;
begin
tst(n);
dbms_output.put_line(to_char(n));
end;
/
The next example uses this table ...
create table t34 (id number not null, col1 date not null)
/
...and toy procedure:
create or replace procedure tst2 (p1 in out nocopy t34%rowtype) is
begin
p1.id := 42;
end;
/
Case 5 : The actual and formal parameters are records, one or both was declared with %ROWTYPE or %TYPE, and constraints on corresponding fields differ.
declare
type r34 is record (id number, dt date);
r r34;
begin
r.id := 23;
r.dt := to_date(null); --trunc(sysdate);
tst2(r);
dbms_output.put_line(to_char(r.id));
end;
/
The next example uses this package spec...
create or replace package pkg is
type r34 is record (id number, dt date);
end;
/
...and toy procedure:
create or replace procedure tst3 (p1 in out nocopy pkg.r34) is
begin
p1.id := p1.id + 10;
end;
/
Case 6: The actual and formal parameters are records, the actual parameter was declared (implicitly) as the index of a cursor FOR LOOP statement, and constraints on corresponding fields differ.
begin
for j in ( select * from t34) loop
tst3(j);
dbms_output.put_line(to_char(j.id));
end loop;
end;
/
The last example uses a remote version of the first procedure.
Case 7: The subprogram is invoked through a database link or as an external subprogram.
declare
n number := 23;
begin
tst#remote_db(n);
dbms_output.put_line(to_char(n));
end;
/
There are working demos of the first six cases on db<>fiddle here.
I have a PL/SQL function.
In that function, there is a variable that changes its value.
I want to take that behavior of value of the variable.
So I want to get the value of that variable line by line.
what is the best way to do this? Or is there any function that I can call?
So you want your function to output multiple outputs which are the different states of your variable during the running of the function.
I dont think there is really any way to do that since functions only have a single output. You could separate the function into a bunch of functions for each step and run them one after the other. Or you could do a procedure with multiple out values, and then at each step when you want to get the value of your variable, you assign the value to one of your out parameters.
Your description is very ... sparse :-) but perhaps you are referring to something like a pipelined function, which can output data as it executes that can then be consumed via SQL. Example below:
SQL> create or replace
2 function my_func return sys.odcinumberlist pipelined as
3 my_var int := 0;
4 begin
5 pipe row ( my_var);
6 my_var := my_var + 10;
7 pipe row ( my_var);
8 my_var := my_var * 10;
9 pipe row ( my_var);
10 return;
11 end;
12 /
Function created.
SQL>
SQL> select * from my_func();
COLUMN_VALUE
------------
0
10
100
but as others have said, we're just guessing here.
Typically variables are declared in the DECLARE section, and are available inside BEGIN block. I find this style stiff and tedious.
Is it possible to declare variables inside the BEGIN block just when they are needed? It is stupid to declare new global variable, if it's needed just to store some temporary value for further calculations, queries and assertions.
Try this - I added comments in the code for you to understand the visibility domain of each variable. Also you can remove the comment from the last DBMS_OUTPUT to see that var2 is no longer available in the outer code.
set serveroutput on;
<<tag>>
DECLARE
var1 INT := 1; -- global variable
BEGIN
DECLARE
var1 INT := 2; -- local variable
var2 INT := 0;
BEGIN
DBMS_OUTPUT.PUT_LINE(var1); -- will display 2 (value of local var1);
DBMS_OUTPUT.PUT_LINE(tag.var1); -- will display 1 (value of global var1);
DBMS_OUTPUT.PUT_LINE(var2); -- will display 0 (value of local var2);
END;
DBMS_OUTPUT.PUT_LINE(var1); -- will display 1 (value of global var1);
-- DBMS_OUTPUT.PUT_LINE(var2); -- will crash since var2 is no longer in memory;
END;
All languages have own features. This is only instrument.
May be you should divide your code into small chunks(procedures, functions)? If your code placed in 20-30 rows, you will not have problems with declaring variables. From smaller chuncks you can build bigger and reusable code.
Regards
it's probably only a misunderstanding of the different types of variables that exist in Oracle SQL and PL/SQL, but how can I use the return value of a PL/SQL function as input for another PL/SQL function inside a SQL script without having to manually set it as value of a DEFINE variable?
Here is the code (being run inside a SQL script inside Oracle SQL Developer):
-- some INSERTS/UPDATES/SELECTS ...
DEFINE in = 'somevalue';
VAR return1 NUMBER;
EXECUTE :return1 := someschema.somepackage.somefunction(in);
PRINT return1;
-- reasonable return value gets printed out
VAR return2 NUMBER;
EXECUTE :return2 := someschema.somefunction(return1);
-- ^
-- this does not work ----------------------+
-- (neither does ":return1")
DEFINE in2 = <manually enter value of "return1">
EXECUTE :return2 := someschema.somefunction(in2);
-- ^
-- this works ------------------------------+
-- some INSERTS/UPDATES/SELECTS ...
Thank's in advance.
DEFINE and EXECUTE would work as expected in SQL*Plus. To execute your entire code in SQL Developer or as a script from a client, I would suggest you:
Use an anonymous block.
DECLARE a variable for the OUT parameter of procedure, and another variable to store the return value of the function.
In the BEGIN-END block, call the procedure.
And use the same varible to store the return value of the function.
For example,
DECLARE
o_var ;
f_var ;
BEGIN
-- call the procedure
package.procedure(o_var);
--call the function
SELECT package.function(o_var) INTO f_var FROM DUAL;
-- do something
END;
/
Below solution works for me.
DECLARE
parameter_var ;
assignment_var ;
BEGIN
--call the function and assign it to assignment_var
assignment_var:=function_xyz(parameter_var);
-- use assignment_var for further computation
END;
/
Hope it helps others.