I want to call a procedure inside another procedure when some conditions met. I am facing a problem when calling the procedure inside another procedure.
Please help me to get this issue resolved.
--- First Procedure ----
create or replace
PROCEDURE first_procedure(
PERIDTYPE IN VARCHAR2,
CITIZENID IN NUMBER,
NIFNUMBER OUT NUMBER,
PERIDNUMBER1 OUT NUMBER,
NUBINUMBER OUT NUMBER ,
REGFORMID OUT NUMBER ,
BOSTATUSCODE OUT VARCHAR2 ,
PERID OUT NUMBER )
AS
BEGIN
SELECT cm_nu_bi
INTO NUBINUMBER
FROM cm_minjus_agt_vw
WHERE cm_id_citizen_stage_sigt = CITIZENID;
select per_id
into PERID
from ci_per_id
WHERE id_type_cd = PERIDTYPE
AND per_id_nbr = NUBINUMBER;
SELECT reg_form_id,
per_id_nbr,
bo_status_cd
INTO REGFORMID,
NIFNUMBER,
BOSTATUSCODE
FROM table_x
WHERE per_id= PERID;
if NIFNUMBER is not null then
INSERT
INTO table_y
(
id_cidadao_stage_sigt,
NU_NIF
)
VALUES
(
PERIDNUMBER,
NIFNUMBER
);
else
EXCE := CM_ERROR_TABLE_ENTRY #CITIZENID=CITIZENID, #REGID=REGFORMID;
end if;
END CM_GET_NIF_NBR;
--- Second Procedure ---
create or replace
PROCEDURE second_procedure
(
CITIZENID IN NUMBER
, REGID IN NUMBER
, EXCEPTIONCATEGORYCD IN OUT VARCHAR2
, MESSAGECATEGORYNUMBER IN OUT NUMBER
, MESSAGENUMBER IN OUT NUMBER
, MESSAGETEXT IN OUT VARCHAR2
) AS
BEGIN
select
message_cat_nbr,
message_nbr,
excp_cat_cd
into
MESSAGECATEGORYNUMBER,
MESSAGENUMBER,
EXCEPTIONCATEGORYCD
from ci_reg_form_excp where
reg_form_id= REGID;
SELECT
message_text
into
MESSAGETEXT
FROM ci_msg_l
WHERE message_cat_nbr=MESSAGECATEGORYNUMBER
AND message_nbr=MESSAGENUMBER
AND language_cd='PTG';
insert INTO tb_sigt_processing_log#MINJUS_AGT_DBLINK
(ID_CIDADAO_STAGE_SIGT,exception_cat_code, message_category, message_number, message_text)
values
(CITIZENID,EXCEPTIONCATEGORYCD,MESSAGECATEGORYNUMBER,MESSAGENUMBER,MESSAGETEXT);
NULL;
END CM_ERROR_TABLE_ENTRY ;
The issue i am getting is. Please tell me if more input is needed.
I think you want to call the procedure with arguments in this line
EXCE := CM_ERROR_TABLE_ENTRY #CITIZENID=CITIZENID, #REGID=REGFORMID;
It is not valid in Oracle. You could just call your procedure using
CM_ERROR_TABLE_ENTRY( CITIZENID,REGFORMID );
Related
I have this simple stored procedure, where it would add a column to my Orders table
create or replace PROCEDURE ADD_ORDER
(
CUSTOMER_ID IN NUMBER
, NEW_ORDER_ID OUT NUMBER
) AS
DECLARE
NEW_ORDER_ID := MAX(ORDERS.ORDER_NO) + 1;
BEGIN
INSERT INTO ORDERS(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES( NEW_ORDER_ID, 36, CUSTOMER_ID, CURDATE(), 'C')
END ADD_ORDER;
It is saying the the declare part is not at the correct place (I think), and also it should not end there. Here is what it is saying at the error screen:
Error(6,1): PLS-00103: Encountered the symbol "DECLARE" when expecting one of the following:
begin function pragma procedure subtype type current cursor delete exists prior external language The symbol "begin was inserted before "DECLARE" to continue.
Error(11,1): PLS-00103: Encountered the symbol "END" when expecting one of the following: , ; return returning
Can anyone tell me what is going wrong here ?
As has been mentioned, it is a bad idea to select the maximum order number and then use that to insert a row. If two processes do this at the same time, they try to insert rows with the same order number.
Better use Oracle's built-in features SEQUENCE or IDENTITY.
Here is how you could create the table:
CREATE TABLE orders
(
order_no NUMBER(8) GENERATED ALWAYS AS IDENTITY,
rep_no NUMBER(3) DEFAULT 36 NOT NULL,
cust_no NUMBER(8) NOT NULL,
order_dt DATE DEFAULT SYSDATE NOT NULL,
status VARCHAR2(1) DEFAULT 'C' NOT NULL
);
And this is what your procedure would look like then:
CREATE OR REPLACE PROCEDURE add_order
(
in_cust_no IN NUMBER,
out_order_no OUT NUMBER
) AS
BEGIN
INSERT INTO ORDERS(cust_no) VALUES (in_cust_no)
RETURNING order_no INTO out_order_no;
END add_order;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=4b49723c15eb810c01077286e171bc95
There is a syntax error in your code.
NEW_ORDER_ID := MAX(ORDERS.ORDER_NO) + 1; --not be used liked it.
Use below code
create or replace PROCEDURE ADD_ORDER
(
CUSTOMER_ID IN NUMBER
, NEW_ORDER_ID OUT NUMBER
) AS
V_NEW_ORDER_ID NUMBER;
BEGIN
SELECT NVL(MAX(ORDER_NO),0)+1 INTO V_NEW_ORDER_ID FROM ORDERS;
INSERT INTO ORDERS(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES( V_NEW_ORDER_ID, 36, CUSTOMER_ID, CURDATE(), 'C');
NEW_ORDER_ID:=V_NEW_ORDER_ID;
/*
* CURDATE() -> I am assuming it is user defined function. You can also use SYSDATE, CURRENT_DATE istead of CURDATE()
* OUT Parameter is a write-only parameter. You cannot read value from OUT Parameter
*/
END ADD_ORDER;
Few things need to be correct.
If you're expecting to write a PROCEDURE or a FUNCTION you don't have to use the DECLARE keyword. In writing a test script or something, you should use the DECLARE keyword to declare variables.
When writing a procedure,
All the parameters should be inside the brackets.
Variables should define between AS and BEGIN keywords and should give the datatype.
If you need to fetch the MAX number of ORDERS TAB you have to write a SQL query for that. Because the MAX function only can be used inside a SQL. Additionally, if you interest there is an in-built feature call SEQUENCE in ORACLE which can use for NEW_ORDER_ID. You can check with the link below.
adding a sequence for oracle plsql
I did some changes to your code. Hope it's working fine now. Please take a visit here.
CREATE or REPLACE PROCEDURE ADD_ORDER (
CUSTOMER_ID IN NUMBER
NEW_ORDER_ID OUT NUMBER
) AS
CURSOR get_max_order_no IS
SELECT MAX(order_no)
FROM ORDERS;
rec_ NUMBER := 0;
BEGIN
OPEN get_max_order_no;
FETCH get_max_order_no INTO rec_;
CLOSE get_max_order_no;
NEW_ORDER_ID := rec_ + 1;
INSERT INTO ORDERS
(ORDER_NO, REP_NO, CUST_NO, ORDER_DT, STATUS)
VALUES
(NEW_ORDER_ID, 36, CUSTOMER_ID, SYSDATE, 'C');
END ADD_ORDER;
I want to pass inputs from an array using a for loop. Finally, I want to return all the outputs from each iteration altogether.
But with this query it just returns the dataset from very last call and overrides the results for the other inputs from the array.
Example: Input: po_array_in :=(123,789, 456)
So it will just return the output from the last input '456' and overrides the results from all other inputs.
Is their any way to get all the outputs for each input altogether? Thanks!
CREATE OR REPLACE PACKAGE Test.return_array_test1
AS
ex_custom EXCEPTION;
TYPE return_array_test1 IS RECORD
(
name1 VARCHAR2 (100),
address1 VARCHAR2 (100),
city VARCHAR2 (100),
state_code VARCHAR2 (100),
zip VARCHAR2 (100),
order_type VARCHAR2 (100) ,
po VARCHAR2 (100)
);
TYPE return_array_test_TBL IS TABLE OF return_array_test1
INDEX BY BINARY_INTEGER;
PROCEDURE array_test_input (
po_array_in IN num_array,
x_comp_rec OUT return_array_test_TBL
);
END return_array_test1;
/
CREATE OR REPLACE PACKAGE BODY Test.return_array_test1
AS
PROCEDURE array_test_input (
po_array_in IN num_array,
x_comp_rec OUT return_array_test_TBL
)IS
BEGIN
FOR i IN 1 .. po_array_in.count
LOOP
select b.name,b.address1,b.city,b.state_code,b.zip,a.order_type,a.po bulk collect into x_comp_rec from headers a,customers b
where a.po = po_array_in(i) and decode(a.deliver_to_site_use_id,null,a.ship_to_site_use_id,a.deliver_to_site_use_id) = b.site_use_id(+)
and '123456' in (substr(a.contract_dealer,1,8),substr(a.install_dealer,1,8),substr(a.ordertaking_dealer,1,8));
END LOOP;
END array_test_input;
END return_array_test1;
/
You don't need to loop over your array; you can use the collection directly and query all of its values at once (depending on how it was defined), with member of if num_array is a schema-level SQL type:
...
BEGIN
select b.name, b.address1, b.city, b.state_code, b.zip, a.order_type,a.po
bulk collect into x_comp_rec
from headers a
left join customers b
on b.site_use_id = coalesce(a.deliver_to_site_use_id, a.ship_to_site_use_id)
where a.po member of po_array_in
and '123456' in (substr(a.contract_dealer,1,8), substr(a.install_dealer,1,8),
substr(a.ordertaking_dealer,1,8));
END array_test_input;
or with a table collection expression if num_array is a schema-level SQL type or, in 12c, is a PL/SQL type:
...
BEGIN
select b.name, b.address1, b.city, b.state_code, b.zip, a.order_type,a.po
bulk collect into x_comp_rec
from table(po_array_in) p
join headers a on a.po = p.column_value
left join customers b
on b.site_use_id = coalesce(a.deliver_to_site_use_id, a.ship_to_site_use_id)
where '123456' in (substr(a.contract_dealer,1,8), substr(a.install_dealer,1,8),
substr(a.ordertaking_dealer,1,8));
END array_test_input;
With this approach the optimiser assumes the collection size is 8k, which in some circumstances can lead to a sub-optimal plan. If you know the number of members will always be significantly lower, you can add a cardinality hint to try to nudge the plan back to what you expect, giving the approximate number of POs you expect to be querying, e.g.:
select /*+ cardinality(p, 10) */ b.name, ...
That hint is undocumented, but it's relatively 'safe'...
i have written my code which is very similar to your code. Below i will be posting my code step by step so that you can run in in your local and understand how you can get all the rows, but not only the last one.
Table Creation
create table tt
(
name1 varchar2(100),
address varchar2(100),
city varchar2(100),
state_code varchar2(100),
zip varchar2(100),
order_type varchar2(100),
po varchar2(100)
);
Insert Script
insert into tt values('nam1','address','city','state_code','zip','order_type','123');
insert into tt values('nam2','address','city','state_code','zip','order_type','789');
Package Spec
create or replace PACKAGE return_array_test1
AS
ex_custom EXCEPTION;
TYPE return_array_test1 IS RECORD
(
name1 VARCHAR2 (100),
address1 VARCHAR2 (100),
city VARCHAR2 (100),
state_code VARCHAR2 (100),
zip VARCHAR2 (100),
order_type VARCHAR2 (100) ,
po VARCHAR2 (100)
);
TYPE return_array_test_TBL IS TABLE OF return_array_test1
INDEX BY BINARY_INTEGER;
type num_array is table of number;
PROCEDURE array_test_input (
po_array_in IN num_array,
x_comp_rec OUT return_array_test_TBL
);
END return_array_test1;
Package Body
CREATE OR REPLACE PACKAGE BODY return_array_test1
AS
PROCEDURE array_test_input(
po_array_in IN num_array,
x_comp_rec OUT return_array_test_TBL )
IS
BEGIN
FOR i IN 1 .. po_array_in.count
LOOP
dbms_output.put_line(i);
SELECT a.name1,
a.address,
a.city,
a.state_code,
a.zip,
a.order_type,
a.po
INTO x_comp_rec(i)
FROM tt a
WHERE a.po = po_array_in(i);
END LOOP;
END array_test_input;
END return_array_test1;
Calling the procedure
set serveroutput on
declare
var_out return_array_test1.return_array_test_TBL;
var_in return_array_test1.num_array;
begin
var_in(1):='123';
var_in(2):='789';
return_array_test1.array_test_input(var_in,var_out);
dbms_output.put_line(var_out(1).name1);
dbms_output.put_line(var_out(2).name1);
end;
Hope this will make you understand.
How do I create a stored procedure which can return multiple rows using SQL Developer BTW.?
Right now stored procedure returns the value for 1 row in 4 diff variables (there are 4 cols)
How I would go about making it so that it could return more than 1 row, for example if i were to query in my date it could return all the relevant data for that date instead of only 1.
create or replace PROCEDURE P2
(
ts IN TIMESTAMP,
u_id OUT VARCHAR2,
u_email OUT VARCHAR2,
cmnt OUT VARCHAR2
)
AS
BEGIN
SELECT U_ID , U_EML, C_TX INTO u_id, u_email, cmnt
FROM U_CM
WHERE U_CM_TS = ts;
END;
ts is the input timestamp
if i put in more a timestamp that has multiple rows associated with it i get an error?
How do i change the design so I can be successful in doing what i want? I am new to this so I dont know where to start
Use ref cursor:
create or replace PROCEDURE P2
(
ts IN TIMESTAMP,
p_result OUT sys_refcursor
)
AS
BEGIN
open p_result for
SELECT U_ID , U_EML, C_TX
FROM U_CM
WHERE U_CM_TS = ts;
END;
Also, try to give a more detailed names to columns and tables for more maintainable code. For example, user_id, user_email instead of u_id, u_eml. What is c_tx? I have no idea. Read about table and column naming conventions.
I’ve a table named QUERIES_DICTIONNARY with 2 columns (ID, SQL_QUERY) the first column is NUMBER and the second column is a Varchar represent an SQL query.
CREATE TABLE "QUERIES_DICTIONNARY"
(
"ID" NUMBER(10,0) NOT NULL ENABLE,
"SQL_QUERY" NVARCHAR2(2000) NOT NULL ENABLE
)
I want to create a stored procedure with 2 parameters (P_KEY, P_RESULTS_CURSOR) where the P_KEY is the ID of my table, and the P_RESULT_CURSOR where i execute my SQL query of the record having ID = P_KEY.
CREATE OR REPLACE PROCEDURE GET_STATISTICS_RESULTS
(
P_KEY IN NUMBER,
P_RESULTS_CURSOR OUT SYS_REFCURSOR
)
In my stored procedure I get the record where the ID = P_KEY
Once I’ve the record I want to execute the the associated query using EXECUTE IMMEDIATE into the P_RESULTS_CURSOR.
CREATE OR REPLACE PROCEDURE GET_STATISTICS_RESULTS
(
P_KEY IN NUMBER
P_RESULTS_CURSOR OUT SYS_REFCURSOR
) AS
BEGIN
---- Get the record
-- SELECT SQL_QUERY FROM QUERIES_DICTIONNARY WHERE ID = P_KEY;
---- Then Execute the query
-- OPEN P_RESULTS_CURSOR FOR ?
END GET_STATISTICS_RESULTS;
How can I do this approach in one Stored procedure please?
There is two ways: simple and not so simple. Simple way is:
CREATE OR REPLACE PROCEDURE GET_STATISTICS_RESULTS
(
P_KEY IN NUMBER;
P_RESULTS_CURSOR OUT SYS_REFCURSOR;
) AS
cursor_text QUERIES_DICTIONNARY.SQL_QUERY%TYPE;
BEGIN
---- Get the record
SELECT SQL_QUERY
INTO cursor_text
FROM QUERIES_DICTIONNARY WHERE ID = P_KEY;
---- Then Execute the query
OPEN P_RESULTS_CURSOR FOR cursor_text;
END GET_STATISTICS_RESULTS;
Not so simple way - using DBMS_SQL package. You can see example in documentation. It will be quite complicated decision.
CREATE OR REPLACE PROCEDURE GET_STATISTICS_RESULTS
(
P_KEY IN NUMBER
P_RESULTS_CURSOR OUT SYS_REFCURSOR
) AS
l_s "QUERIES_DICTIONNARY"."SQL_QUERY"%TYPE;
BEGIN
---- Get the record
SELECT SQL_QUERY into l_s FROM QUERIES_DICTIONNARY WHERE ID = P_KEY;
---- Then Execute the query
OPEN P_RESULTS_CURSOR FOR l_s;
END GET_STATISTICS_RESULTS;
PLS-00382 appears because of NVARCHAR2 column. You may try to cast nvarchar2 to varchar2:
l_s VARCHAR2(4000);
...
SELECT CAST(SQL_QUERY AS VARCHAR2(4000)) into l_s FROM QUERIES_DICTIONNARY WHERE ID = P_KEY;
...
But you may lose some data.
I am a newbie to Oracle and this is my first post for Oracle queries.
Below is the existing query which inserts 1 row for each SP call.
I want to make change in the SP which would accept input as array where SAP system would would send the Array to Stored Procedure.
As you observe in SP, the value of ID is incremented each time with the each update. The SP will take this input of Phone and Text and insert the value of ID in sequence wise.The ID is not passed in the input.
CREATE OR REPLACE PROCEDURE DetailsTable
(
Phoneno IN NUMBER,
Text IN VARCHAR2
)
aS
BEGIN
INSERT INTO PERSON.DETAILS(
ID,
PHONENO,
TEXT,
COUNTRY,
LANG,
--PRIORITY,
SENDER)
VALUES (
DETAILS_seq.nextval ,
p_phoneno,
p_text ,
'RSA',
'EN',
'Nest-Payroll');
commit;
END DetailsTable;
Please guide.
SQL> CREATE OR REPLACE TYPE arraytype AS VARRAY(1000) OF VARCHAR2(100);
2 /
Type created
SQL> CREATE OR REPLACE PROCEDURE test_array (in_array arraytype) IS
2 BEGIN
3 FOR i IN 1..in_array.count LOOP
4 DBMS_OUTPUT.PUT_LINE(in_array(i));
5 END LOOP;
6 END;
7 /
Procedure created
SQL> DECLARE
2 var_array arraytype;
3 BEGIN
4 var_array := arraytype();
5 var_array.EXTEND(10);
6 var_array(1) := '1st sentence in the array';
7 var_array(2) := '2nd sentence in the array';
8 test_array(var_array);
9 END;
10 /
1st sentence in the array
2nd sentence in the array
We can use a Type in SQL but it needs to be declared as a SQL Type:
create or replace type person_t as object
(phoneno number
, text varchar2(100)
);
/
create or replace type person_nt as table of person_t
/
Use it like this:
CREATE OR REPLACE PROCEDURE DetailsTable
(
p_array in person_nt
)
aS
BEGIN
INSERT INTO PERSON.DETAILS(
ID,
PHONENO,
TEXT,
COUNTRY,
LANG,
--PRIORITY,
SENDER)
select DETAILS_seq.nextval ,
t.phoneno,
t.text ,
'RSA',
'EN',
'Nest-Payroll'
from table (p_array)t;
commit;
END DetailsTable;
/