I have a stored procedure with an IN OUT parameter declared like follows:
create or replace PROCEDURE RIFATT_SEGN0_INS(pIdRifattSegn0 in OUT NUMBER,
pNumDossier IN VARCHAR2 ,
pNumConsegna IN NUMBER,
pDtConsegna IN DATE,
[..]
) AS
[..]
Whenever i call it from another procedure, how do i get the pIdRifattSegn0 parameter that is also out?
Your question isn't entirely clear. An IN OUT parameter is passed both ways, as its name implies. This means it has to be passed a variable, not a literal and you need a declare block to do that. For example:
declare
l_segn number;
begin
l_segn := 1;
-- procedure will have received value = 1
rifatt_segn0_ins(l_segn, 'x', 2, sysdate);
-- procedure may have changed value of l_segn from 1 to something else
dbms_output.put_line(l_segn);
end;
Here is an example:
SQL> create or replace PROCEDURE RIFATT_SEGN0_INS
2 ( pIdRifattSegn0 IN OUT NUMBER
3 , pNumDossier IN VARCHAR2
4 , pNumConsegna IN NUMBER
5 , pDtConsegna IN DATE
6 )
7 as
8 begin
9 dbms_output.put_line(pNumDossier);
10 dbms_output.put_line(to_char(pNumConsegna));
11 dbms_output.put_line(to_char(pDtConsegna,'yyyy-mm-dd'));
12 pIdRifattSegn0 := sqrt(pIdRifattSegn0);
13 end;
14 /
Procedure is aangemaakt.
SQL> create or replace procedure another_procedure
2 as
3 l_IdRifattSegn0 number := 4;
4 begin
5 rifatt_segn0_ins
6 ( pIdRifattSegn0 => l_IdRifattSegn0
7 , pNumDossier => '1A'
8 , pNumConsegna => 42
9 , pDtConsegna => sysdate
10 );
11 dbms_output.put_line('from another_procedure: l_IdRifattSegn0 = ' || to_char(l_IdRifattSegn0));
12 end;
13 /
Procedure is aangemaakt.
SQL> exec another_procedure
1A
42
2009-05-21
from another_procedure: l_IdRifattSegn0 = 2
PL/SQL-procedure is geslaagd.
Regards,
Rob.
Related
I'm trying to calling 2 variables inside another procedure that should give me the min and max in a column like this:
create or replace procedure ClassEnrollmentReport
(p_CLASSNAME in class.classname%TYPE)
as
begin
dbms_output.put_line('Max gpa:');
StudentWithGivenGPA(MinMaxGPA.p_maxStudentGPA(p_CLASSNAME));
dbms_output.put_line('Min gpa:');
StudentWithGivenGPA(MinMaxGPA.p_minStudentGPA(p_CLASSNAME));
end ClassEnrollmentReport;
Which gives me an error message like this:
6/5 PL/SQL: Statement ignored
6/35 PLS-00225: subprogram or cursor 'MINMAXGPA' reference is out of scope
8/5 PL/SQL: Statement ignored
8/35 PLS-00225: subprogram or cursor 'MINMAXGPA' reference is out of scope
Here's how the minmaxgpa procedure look like:
create or replace procedure MinMaxGPA
(
p_CLASSNAME in class.classname%type,
p_maxStudentGPA OUT student.gpa%type,
p_minStudentGPA OUT student.gpa%type
)
as
maxStudentGPA student.gpa%type;
minStudentGPA student.gpa%type;
begin
select max(gpa) into maxStudentGPA
from student
where classno = (select classno from class where upper(classname) = upper(p_CLASSNAME));
select min(gpa) into minStudentGPA
from student
where classno = (select classno from class where upper(classname) = upper(p_CLASSNAME));
p_maxStudentGPA := maxStudentGPA;
p_minStudentGPA := minStudentGPA;
end MinMaxGPA;
I know how to do this with a function, but having no idea how can I get this with procedure. Can you help me with this?
With really simple sample tables (just to make the procedure compile):
SQL> create table class as select 'abc' classname, 100 clasno from dual;
Table created.
SQL> create table student as select 1 gpa, 100 classno from dual;
Table created.
Procedure code (I didn't change it):
SQL> create or replace procedure MinMaxGPA
2 (
3 p_CLASSNAME in class.classname%type,
4 p_maxStudentGPA OUT student.gpa%type,
5 p_minStudentGPA OUT student.gpa%type
6
7 )
8 as
9 maxStudentGPA student.gpa%type;
10 minStudentGPA student.gpa%type;
11 begin
12 select max(gpa) into maxStudentGPA
13 from student
14 where classno = (select classno from class where upper(classname) = upper(p_CLASSNAME));
15
16
17 select min(gpa) into minStudentGPA
18 from student
19 where classno = (select classno from class where upper(classname) = upper(p_CLASSNAME));
20
21 p_maxStudentGPA := maxStudentGPA;
22 p_minStudentGPA := minStudentGPA;
23 end MinMaxGPA;
24 /
Procedure created.
Your "new" ClassEnrollmentReport should then look like this: you have to follow the MinMaxGPA procedure's description - it accepts 3 parameters (one in, two out):
SQL> create or replace procedure ClassEnrollmentReport
2 (p_CLASSNAME in class.classname%TYPE)
3 as
4 v_mingpa number;
5 v_maxgpa number;
6 begin
7 minmaxgpa(p_classname, v_mingpa, v_maxgpa);
8 dbms_output.put_line('Max gpa: ' || v_maxgpa);
9 dbms_output.put_line('Min gpa: ' || v_mingpa);
10 end ClassEnrollmentReport;
11 /
Procedure created.
If we test it:
SQL> set serveroutput on
SQL> exec classenrollmentreport('abc');
Max gpa: 1
Min gpa: 1
PL/SQL procedure successfully completed.
SQL>
I need some help for figuring out how to use the CURRENT_DATE if the p_start_time is NULL when doing an insert.
The code below is what I have figured out so far (except for the insert statement). The code I need help for is the last code block.
Using PL/SQL btw.
create or replace procedure BEGIN_TRIP_SP (
p_trip_id OUT INTEGER, -- an output parameter
p_bicycle_id IN INTEGER, -- Must not be NULL. Must match value value in BC_BICYCLE and BC_DOCK tables.
p_start_time IN DATE, -- If NULL, use CURRENT_DATE system date value
p_membership_id IN INTEGER -- Must not be NULL. Must match value in BC_MEMBERSHIP table.
)
IS
lv_membership_id_exist INTEGER;
lv_bicycle_id_exist INTEGER;
lv_Error_Message VARCHAR2(200);
ex_Exception EXCEPTION;
BEGIN
IF p_bicycle_id IS NULL THEN
lv_Error_Message := 'Missing mandatory value for bicycle id in BEGIN_TRIP_SP. No trip added.';
RAISE ex_Exception;
END IF;
IF p_start_time IS NULL THEN
NVL(trip_start_time, '08-03-2022')
END IF;
INSERT INTO bc_trip (
Trip_id,
Bicycle_id,
Trip_start_time,
Membership_id
)
VALUES (
p_trip_id,
p_bicycle_id,
NVL(p_start_time, current_date),
p_membership_id
);
See if this helps; I presumed trip_id is somehow automatically generated so you'll be just returning its value to the caller.
Sample table:
SQL> CREATE TABLE bc_trip
2 (
3 trip_id NUMBER GENERATED ALWAYS AS IDENTITY,
4 bicycle_id NUMBER,
5 trip_start_time DATE,
6 membership_id NUMBER
7 );
Table created.
Procedure:
SQL> CREATE OR REPLACE PROCEDURE begin_trip_sp
2 (p_trip_id OUT INTEGER, -- an output parameter
3 p_bicycle_id IN INTEGER, -- Must not be NULL. Must match value value in BC_BICYCLE and BC_DOCK tables.
4 p_start_time IN DATE, -- If NULL, use CURRENT_DATE system date value
5 p_membership_id IN INTEGER -- Must not be NULL. Must match value in BC_MEMBERSHIP table.
6 )
7 IS
8 BEGIN
9 IF p_bicycle_id IS NULL
10 THEN
11 raise_application_error (
12 -20000,
13 'Missing mandatory value for bicycle id in BEGIN_TRIP_SP. No trip added.');
14 ELSE
15 INSERT INTO bc_trip (bicycle_id, trip_start_time, membership_id)
16 VALUES (p_bicycle_id,
17 NVL (p_start_time, CURRENT_DATE),
18 p_membership_id)
19 RETURNING trip_id
20 INTO p_trip_id;
21 END IF;
22 END;
23 /
Procedure created.
Testing:
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_trip_id NUMBER;
3 BEGIN
4 begin_trip_sp (l_trip_id,
5 1,
6 NULL,
7 100);
8 DBMS_OUTPUT.put_line ('Inserted trip ID = ' || l_trip_id);
9 END;
10 /
Inserted trip ID = 1
PL/SQL procedure successfully completed.
What if there's no P_BICYCLE_ID?
SQL> l5
5* 1,
SQL> c/1/null/
5* null,
SQL> /
DECLARE
*
ERROR at line 1:
ORA-20000: Missing mandatory value for bicycle id in BEGIN_TRIP_SP. No trip
added.
ORA-06512: at "DP_4005.BEGIN_TRIP_SP", line 11
ORA-06512: at line 4
SQL>
Result is
SQL> SELECT * FROM bc_trip;
TRIP_ID BICYCLE_ID TRIP_START MEMBERSHIP_ID
---------- ---------- ---------- -------------
1 1 08.03.2022 100
SQL>
Just write default value for that parameter:
create or replace procedure BEGIN_TRIP_SP (
p_trip_id OUT INTEGER,
p_bicycle_id IN INTEGER,
p_start_time IN DATE DEFAULT sysdate,--if the parameter is not given it will take sysdate (current date)
p_membership_id IN INTEGER
)...
I have a situation here:
We need to run procedure_1 in a package when a particular column say column X has data in it and simultaneously run another procedure_2 in package if there is no data in that column X
Can anyone please advice on what can be done using Oracle ?
Something like this?
table DECIDE contains PARTICULAR_COLUMN
package has two simple procedures which do nothing; they just identify themselves
.
SQL> create table decide
2 (id number,
3 particular_column varchar2(1));
Table created.
SQL> insert into decide values (1, 'X');
1 row created.
SQL> insert into decide values (2, null);
1 row created.
SQL> create or replace package pkg_decide as
2 procedure p1;
3 procedure p2;
4 end;
5 /
Package created.
SQL> create or replace package body pkg_decide as
2 procedure p1 is
3 begin
4 dbms_output.put_line('-> running proc 1');
5 end;
6
7 procedure p2 is
8 begin
9 dbms_output.put_line('-> running proc 2');
10 end;
11 end;
12 /
Package body created.
SQL>
Testing: if the PARTICULAR_COLUMN isn't empty, P1 will run; otherwise, P2. That can be done with dynamic SQL, i.e. EXECUTE IMMEDIATE. As we're going to run a procedure, we have to form a full anonymous PL/SQL block (begin - procedure name - end).
P.S. As suggested by Matthew, a simple IF will do.
SQL> begin
2 for cur_r in (select particular_column,
3 case when particular_column is not null then 'pkg_decide.p1'
4 else 'pkg_decide.p2'
5 end prc_name
6 from decide
7 ) loop
8 dbms_output.put_line('particular column = ' || cur_r.particular_column ||
9 ', should run procedure ' || cur_r.prc_name);
10 execute immediate 'begin ' ||cur_r.prc_name ||'; end;';
11
12 -- Or, as suggested by Matthew, a simple IF will do
13 if cur_r.particular_column is not null then
14 pkg_decide.p1;
15 else
16 pkg_decide.p2;
17 end if;
18 end loop;
19 end;
20 /
particular column = X, should run procedure pkg_decide.p1
-> running proc 1
-> running proc 1
particular column = , should run procedure pkg_decide.p2
-> running proc 2
-> running proc 2
PL/SQL procedure successfully completed.
SQL>
I am passing arguments `EBN,BGE' into a procedure , then I am passing this argument to a cursor.
create or replace procedure TEXT_MD (AS_IDS VARCHAR2)
is
CURSOR C_A (AS_ID VARCHAR2) IS
SELECT
name
FROM S_US
WHERE US_ID IN (AS_ID);
BEGIN
FOR A IN C_A (AS_IDS) LOOP
DBMS_OUTPUT.PUT_LINE('I got here: '||AS_IDS);
end loop;
END;
But while debuging the count of the cursor is still null
So my question , why the cursor not returning values with in condition
You are passing a string parameter, so it will be used as a string, not as a list of strings; so, your cursor will be something like
SELECT name
FROM S_US
WHERE US_ID IN ('EBN,BGE')
This will, of course, not do what you need.
You may need to change your procedure and the way to pass parameters; if you want to keep a string parameter , one way could be the following:
setup:
SQL> CREATE TABLE S_US
2 (
3 US_ID,
4 NAME
5 ) AS
6 SELECT 'EBN', 'EBN name' FROM DUAL
7 UNION ALL
8 SELECT 'BGE', 'BGE name' FROM DUAL;
Table created.
procedure:
SQL> CREATE OR REPLACE PROCEDURE TEXT_MD_2(AS_IDS VARCHAR2) IS
2 vSQL varchar2(1000);
3 c sys_refcursor;
4 vName varchar2(16);
5 BEGIN
6 vSQL := 'SELECT name
7 FROM S_US
8 WHERE US_ID IN (' || AS_IDS || ')';
9 open c for vSQL;
10 loop
11 fetch c into vName;
12 if c%NOTFOUND then
13 exit;
14 end if;
15 DBMS_OUTPUT.PUT_LINE(vName);
16 END LOOP;
17 END;
18 /
Procedure created.
You need to call it with a string already formatted to be a parameter list for IN:
SQL> EXEC TEXT_MD_2('''EBN'',''BGE''');
EBN name
BGE name
PL/SQL procedure successfully completed.
This is only an example of a possible way, and not the way I would do this.
Among the reasons to avoud this kind of approach, consider what Justin Cave says:
"that would be a security risk due to SQL injection and would have a potentially significant performance penalty due to constant hard parsing".
I believe you should better check how to pass a list of values to your procedure, rather then using a string to represent a list of strings.
Here is a possible way to do the same thing with a collection:
SQL> CREATE OR REPLACE TYPE tabVarchar2 AS TABLE OF VARCHAR2(16)
2 /
Type created.
SQL>
SQL> CREATE OR REPLACE PROCEDURE TEXT_MD_3(AS_IDS tabVarchar2) IS
2 vSQL VARCHAR2(1000);
3 c SYS_REFCURSOR;
4 vName VARCHAR2(16);
5 BEGIN
6 FOR i IN (SELECT name
7 FROM S_US INNER JOIN TABLE(AS_IDS) tab ON (tab.COLUMN_VALUE = US_ID))
8 LOOP
9 DBMS_OUTPUT.PUT_LINE(i.name);
10 END LOOP;
11 END;
12 /
Procedure created.
SQL>
SQL> DECLARE
2 vList tabVarchar2 := NEW tabVarchar2();
3 BEGIN
4 vList.EXTEND(2);
5 vList(1) := 'BGE';
6 vList(2) := 'EBN';
7 TEXT_MD_3(vList);
8 END;
9 /
BGE name
EBN name
PL/SQL procedure successfully completed.
SQL>
Again, you can define collections in different ways, within a stored procedure or not, indexed or not, and so on; this is only one of the possible ways, not necessarily the best, depending on your environment, needs.
How can we define output parameter size in stored procedure?
You can't. Of course, you are in control of how much data you put into the OUT parameter in the stored procedure. If you want you can create a sized local variable to hold the data and then assign the value of that variable to the OUT parameter.
The calling program determines the size of the variable that receives the OUT parameter.
Here is a simple package which declares and uses a subtype:
SQL> create or replace package my_pkg as
2 subtype limited_string is varchar2(10);
3 procedure pad_string (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string);
6 end my_pkg;
7 /
Package created.
SQL> create or replace package body my_pkg as
2 procedure pad_string
3 (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string)
6 as
7 begin
8 p_out_str := rpad(p_in_str, p_length, 'A');
9 end pad_string;
10 end my_pkg;
11 /
Package body created.
SQL>
However, if we call PAD_STRING() in such a way that the output string exceeds the subtype's precision it still completes successfully. Bother!
SQL> var out_str varchar2(128)
SQL>
SQL> exec my_pkg.pad_string('PAD THIS!', 12, :out_str)
PL/SQL procedure successfully completed.
SQL>
SQL> select length(:out_str) from dual
2 /
LENGTH(:OUT_STR)
----------------
12
SQL>
This is annoying but it's the way PL/SQL works so we have to live with it.
The way to resolve the situaton is basically to apply DBC principles and validate our parameters. So, we can assert business rules against the inputs like this:
SQL> create or replace package body my_pkg as
2 procedure pad_string
3 (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string)
6 as
7 begin
8 if length(p_in_str) + p_length > 10 then
9 raise_application_error(
10 -20000
11 , 'Returned string cannot be longer than 10 characters!');
12 end if;
13 p_out_str := rpad(p_in_str, p_length, 'A');
14 end pad_string;
15 end my_pkg;
16 /
Package body created.
SQL>
SQL> exec my_pkg.pad_string('PAD THIS!', 12, :out_str)
BEGIN my_pkg.pad_string('PAD THIS!', 12, :out_str); END;
*
ERROR at line 1:
ORA-20000: Returned string cannot be longer than 10 characters!
ORA-06512: at "APC.MY_PKG", line 9
ORA-06512: at line 1
SQL>
Or we can assert business rules against the output like this:
SQL> create or replace package body my_pkg as
2 procedure pad_string
3 (p_in_str varchar
4 , p_length number
5 , p_out_str out limited_string)
6 as
7 l_str limited_string;
8 begin
9 l_str := rpad(p_in_str, p_length, 'A');
10 p_out_str := l_str;
11 end pad_string;
12 end my_pkg;
13 /
Package body created.
SQL>
SQL> exec my_pkg.pad_string('PAD THIS!', 12, :out_str)
BEGIN my_pkg.pad_string('PAD THIS!', 12, :out_str); END;
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "APC.MY_PKG", line 9
ORA-06512: at line 1
SQL>
In most scenarios we should do both. This is the polite way to build interfaces, because it means other routines can call our procedures with the confidence that they will return the values they say they will.
You could use a subtype in a package header and type check that in the body...
CREATE OR REPLACE PACKAGE my_test
AS
SUBTYPE my_out IS VARCHAR2( 10 );
PROCEDURE do_something( pv_variable IN OUT my_out );
END;
/
CREATE OR REPLACE PACKAGE BODY my_test
AS
PROCEDURE do_something( pv_variable IN OUT my_out )
IS
lv_variable my_out;
BEGIN
-- Work on a local copy of the variable in question
lv_variable := 'abcdefghijklmnopqrstuvwxyz';
pv_variable := lv_variable;
END do_something;
END;
/
Then when you run this
DECLARE
lv_variable VARCHAR2(30);
BEGIN
my_test.do_something( lv_variable );
DBMS_OUTPUT.PUT_LINE( '['||lv_variable||']');
END;
/
You would get the error
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
Seems to go against the spirit of using an out parameter, but after Tony's comment this was the only thing I could think of to control data within the called code.