I need help with the following:
I have the following table:
CREATE TABLE LOG_PUSH_READOUTS_HEADERS
(
ID NUMBER NOT NULL,
PUSH_DATE DATE NOT NULL,
SOURCE_SERIAL VARCHAR2(100) NOT NULL,
SOURCE_START_DATE DATE NOT NULL,
SOURCE_END_DATE DATE NOT NULL,
SOURCE_RUS_TYPE_ID NUMBER,
OUTPUT_SERIAL VARCHAR2(100) NOT NULL,
FILTERS_RUS VARCHAR2(100),
FILTERS_INDICATORS VARCHAR2(100),
CONSTRAINT id_pk PRIMARY KEY (ID)
);
I need to implement the following procedure:
Input parameters for inserting in a table are all columns except ID for which we need to create a sequencer. Output parameters are ID and RESULT_CODE. ID is a value of primary key that is inserted. RESULT_CODE is zero if procedure is successful, or some value in an interval 9000-9999. Use RESULT_CODE for errors, for example, RESULT_CODE 9123 is 'FILTER_RUS cannot be null.'
Here is my attempt:
CREATE OR REPLACE PROCEDURE INSERT_HEADER
(PUSH_DATE IN DATE, SOURCE_SERIAL IN VARCHAR2, SOURCE_START_DATE IN DATE, SOURCE_END_DATE IN DATE, SOURCE_RUS_TYPE_ID IN NUMBER,
OUTPUT_SERIAL IN VARCHAR2, FILTERS_RUS IN VARCHAR2, FILTERS_INDICATORS IN VARCHAR2, ID OUT NUMBER, RESULT_CODE OUT NUMBER)
IS
hd_seq NUMBER;
BEGIN
SELECT AMM_MDM.Header_Seq.NEXTVAL INTO hd_seq FROM DUAL;
ID:=hd_seq;
INSERT INTO AMM_MDM.LOG_PUSH_READOUTS_HEADERS (PUSH_DATE IN DATE, SOURCE_SERIAL IN VARCHAR2, SOURCE_START_DATE IN DATE,
SOURCE_END_DATE IN DATE, SOURCE_RUS_TYPE_ID IN NUMBER,
OUTPUT_SERIAL IN VARCHAR2, FILTERS_RUS IN VARCHAR2, FILTERS_INDICATORS IN VARCHAR2)
VALUES (PUSH_DATE, SOURCE_SERIAL, SOURCE_START_DATE,
SOURCE_END_DATE, SOURCE_RUS_TYPE_ID,
OUTPUT_SERIAL, FILTERS_RUS, FILTERS_INDICATORS)
END;
How to set RESULT_CODE in an interval 9000-9999? How to manage errors?
The way I understood the question, it would be like the following example. I shortened column list (didn't feel like tying that much), but you should be able to get the general idea.
create or replace procedure p_insert_header
(p_push_date in date,
p_source_serial in varchar2,
p_filter_rus in varchar2,
--
p_id out number,
p_result_code out number
)
is
l_id number;
begin
-- check for errors
if p_push_date is null then
p_result_code := 1234;
elsif p_source_serial not in ('A', 'B', 'C') then
p_result_code := 5532;
elsif p_filter_rus is null then
p_result_code := 9123;
end if;
-- if there are no errors, do insert
if p_result_code is null then
-- fetch sequence number
p_id := header_seq.nextval;
insert into log_push_readouts_headers
(id, push_date, source_serial, filter_rus)
values
(p_id, p_push_date, p_source_serial, p_filter_rus);
end if;
end;
At the end, once the procedure finishes, it'll return - as OUT parameters:
ID:
some sequence number, if everything was OK
NULL, if it was not
RESULT_CODE:
NULL, if everything was OK
error number, if it was not
For user defined errors, Oracle permits negative integers in the range -20000 .. -20999. You need to remove data type declarations for the column list of the insert statement
CREATE OR REPLACE PROCEDURE INSERT_HEADER(PUSH_DATE IN DATE,
SOURCE_SERIAL IN VARCHAR2,
SOURCE_START_DATE IN DATE,
SOURCE_END_DATE IN DATE,
SOURCE_RUS_TYPE_ID IN NUMBER,
OUTPUT_SERIAL IN VARCHAR2,
FILTERS_RUS IN VARCHAR2,
FILTERS_INDICATORS IN VARCHAR2,
ID OUT NUMBER,
RESULT_CODE OUT NUMBER)
IS
BEGIN
INSERT INTO LOG_PUSH_READOUTS_HEADERS
(ID,
PUSH_DATE,
SOURCE_SERIAL,
SOURCE_START_DATE,
SOURCE_END_DATE,
SOURCE_RUS_TYPE_ID,
OUTPUT_SERIAL,
FILTERS_RUS,
FILTERS_INDICATORS)
VALUES
(Header_Seq.NEXTVAL,
PUSH_DATE,
SOURCE_SERIAL,
SOURCE_START_DATE,
SOURCE_END_DATE,
SOURCE_RUS_TYPE_ID,
OUTPUT_SERIAL,
FILTERS_RUS,
FILTERS_INDICATORS);
--Suppose some opertions performed and gave the RESULT_CODE value below
RESULT_CODE := 20123;
IF RESULT_CODE = 20123 THEN raise_application_error(-RESULT_CODE, 'FILTER_RUS cannot be null');
--ELSIF RESULT_CODE = 20124 THEN raise_application_error(-RESULT_CODE, 'Some other message 1');
--ELSIF RESULT_CODE = 20125 THEN raise_application_error(-RESULT_CODE, 'Some other message 2');
END IF;
END;
In this case it hurls with the message : FILTER_RUS cannot be null
Related
Mostly I avoid table variables as input parameters for a stored procedure. Because I do not know how to handle them, but in this case I have no other option. I have a requirement where hundreds of records will be passed on to database from Oracle Agile PLM. What I have to do is to populate a table from the input records/list. For accomplishing this I have developed an object type and then a table type out of that object type.
CREATE OR REPLACE TYPE TEST_USER.MD_TYPE AS OBJECT
(QUERY_REF VARCHAR2 (1000 BYTE),
COL_NAME VARCHAR2 (100 BYTE),
COL_LENGTH VARCHAR2 (50 BYTE),
COL_SEQ NUMBER)
/
CREATE OR REPLACE TYPE TEST_USER.MD_TYPE_TABLE AS TABLE OF MD_TYPE
/
Stored Procedure:
CREATE OR REPLACE PROCEDURE SP_TEST2
(
P_MD_TABLE IN MD_TYPE_TABLE,
p_success OUT number
)
IS
BEGIN
INSERT INTO MDATA_TABLE
(
QUERY_REF ,
COL_NAME ,
COL_LENGTH ,
COL_SEQ
)
SELECT ea.*
FROM TABLE(P_MD_TABLE) ea;
p_success :=1;
EXCEPTION
WHEN OTHERS THEN
p_success := -1;
END SP_TEST2;
The problem is I do not know how to populate, first parameter P_MD_TABLE and then MDATA_TABLE. And the procedure compiles without any errors. I have not tested this procedure.
Any help please.
Procedure for loading MD_TYPE_TABLE by passing parameters to MD_TYPE
CREATE OR REPLACE PROCEDURE SP_UPLOAD_MD_TYPE
(
P_QUERY_REF VARCHAR2,
P_COL_NAME VARCHAR2,
P_COL_LENGTH VARCHAR2,
p_col_seq NUMBER,
p_no_of_rows_to_insert NUMBER,
p_num OUT NUMBER
)
IS
p_type_tbl MD_TYPE_TABLE := MD_TYPE_TABLE(); --initialize
BEGIN
<<vartype>>
FOR i IN 1..p_no_of_rows_to_insert
LOOP
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE(P_QUERY_REF, P_COL_NAME, P_COL_LENGTH, p_col_seq);
END LOOP vartype;
SP_TEST2(p_type_tbl, p_num);
END;
You can populate a table type by using extend/ bulk collect
using extend
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE('QUERY_REF1', 'COL_NAME1', 'COL_LENGTH1', 1);
or using bulk collect
SELECT MD_TYPE(c1, c2... cn)
BULK COLLECT INTO p_type_tbl
FROM some_table;
Demo
DECLARE
p_type_tbl MD_TYPE_TABLE := MD_TYPE_TABLE(); --initialize
p_num NUMBER;
BEGIN
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE('QUERY_REF1', 'COL_NAME1', 'COL_LENGTH1', 1);
p_type_tbl.extend();
p_type_tbl(p_type_tbl.last) := MD_TYPE('QUERY_REF2', 'COL_NAME2', 'COL_LENGTH2', 2);
SP_TEST2(p_type_tbl, p_num);
DBMS_OUTPUT.PUT_LINE(p_num);
END;
/
OutPut
1
SELECT * FROM MDATA_TABLE;
OutPut
QUERY_REF COL_NAME COL_LENGTH COL_SEQ
QUERY_REF1 COL_NAME1 COL_LENGTH1 1
QUERY_REF2 COL_NAME2 COL_LENGTH2 2
Dear Oracle Developers,
I have searched and googled to find a solution for my problem but nothing helped me.
Situation :
TABLE : CUSTOMER(....);
My problem is : I want to create a stored procedure say get_customers to return an array of customer rows. I have no idea how to get this working.
I tried to create a type customer_rec and using a cursor to retrieve no more than maxRows
create or replace procedure get_customers(maxRows IN NUMBER, ??? OUT ????)
How to define the OUT parameter ?
How to retrieve the rows in the array using a cursor ?
Thanks a lot
I would like to answer this in a way that discourages passing around arrays, when passing around cursors is a more sound approach. It doesn't exactly answer the question as posed, but it is an answer. Thinking cursors instead of thinking arrays is more efficient and thus more scalable. Also, it can be much easier code to maintain.
create table customer (
customer_id number(2) primary key,
customer_name varchar2(200) );
insert into customer values (1, 'Customer One');
insert into customer values (2, 'Customer Two');
insert into customer values (3, 'Customer Three');
insert into customer values (4, 'Customer Four');
insert into customer values (5, 'Customer Five');
insert into customer values (6, 'Customer Six');
insert into customer values (7, 'Customer Seven');
CREATE OR REPLACE PACKAGE cursor_not_array IS
FUNCTION get_customers(p_max_records INTEGER, p_id_start INTEGER, p_id_end INTEGER DEFAULT NULL) RETURN SYS_REFCURSOR;
END cursor_not_array;
CREATE OR REPLACE PACKAGE BODY cursor_not_array IS
c_max_customer_id CONSTANT NUMBER(2) := 99;
FUNCTION get_customers(p_max_records INTEGER, p_id_start INTEGER, p_id_end INTEGER DEFAULT NULL) RETURN SYS_REFCURSOR IS
v_result SYS_REFCURSOR;
BEGIN
OPEN v_result FOR
SELECT customer_id,
customer_name
FROM customer
WHERE customer_id BETWEEN p_id_start AND nvl(p_id_end, c_max_customer_id)
ORDER BY customer_id;
RETURN v_result;
END;
END cursor_not_array;
You could create a package like this:
create or replace package customers is
type customers_array is table of customer%rowtype index by binary_integer;
procedure get_customers(maxRows IN NUMBER, customer_array OUT customers_array);
end customers;
create or replace package body customers is
procedure get_customers(maxRows IN NUMBER, customer_array OUT customers_array) is
cursor c_customers is
select *
from customers;
where rownum <= maxRows;
i number := 1;
begin
for r in c_customers loop
customer_array(i) := r;
i := i + 1;
end loop;
end get_customers;
end customers;
And then call the get_customers procedure from wherever you want to...
first time create VARRAY type.
'create TYPE CUSTARRAY is VARRAY(100) OF VARCHAR2(30);'
varray limit is depends on you.
then create procedure that return CUSTARRAY type parameter.
`create
procedure prc_get_arr(p_maxrow in number, p_customers out custarray)
as
my_cust custarray := custarray();
cursor c_cust is select name from CUSTOMER where rownum<p_maxrow;
v_customer varchar2(64);
begin
open c_cust;
loop
fetch c_cust into v_customer;
exit when c_cust%notfound;
my_cust.extend;
my_cust(my_cust.count) := v_customer;
end loop;
close c_cust;
p_customers:=my_cust;
end;`
Now call this procedure
DECLARE
P_MAXROW NUMBER;
p_customers custarray;
v_cnt number:=0;
begin
P_MAXROW := 22;
prc_get_arr( p_maxrow => p_maxrow, p_customers => p_customers );
v_cnt:=p_customers.count;
for i in p_customers.first..p_customers.last loop
dbms_output.put_line('P_CUSTOMERS = ' || p_customers(i));
end loop;
end;
You can, using the SYS_REFCURSOR on your function output.
First you have to define a collection :
TYPE customers_array IS TABLE OF customer%ROWTYPE
INDEX BY BINARY_INTEGER;
Then your procedure simply have to fetch the result into that collection.
You're procedure could be written as follow:
CREATE OR REPLACE PACKAGE your_pkg
AS
TYPE customers_array IS TABLE OF customer%ROWTYPE
INDEX BY BINARY_INTEGER;
PROCEDURE get_customers(pn_max_rows IN NUMBER,
pt_coustomers OUT customers_array);
END your_pkg;
CREATE OR REPLACE PACKAGE BODY your_pkg
AS
PROCEDURE get_customers(pn_max_rows IN NUMBER,
pt_coustomers OUT customers_array)
IS
BEGIN
SELECT *
BULK COLLECT INTO pt_coustomers
FROM customers
WHERE rownum <= pn_max_rows;
END get_customers;
END your_pkg;
I used Oracle 11G database, i have a LOG table
when i write in this table with this function:
Pk_Util.LOG_ERROR('Pk_Slt.IMPORT_LIAISON', 'LIAISON', NULL, liaison_code, NULL, SUBSTR(SQLERRM, 1, 200), SQLCODE);
I have this in my LOG table:
30/04/13,'Pk_Slt.IMPORT_LIAISON','LIAISON',null,'
$ยจ%r?',null,'ORA-06502: PL/SQL','-6502','5484973'
My LOG_ERROR procedure:
PROCEDURE LOG_ERROR(
nom_procedure IN VARCHAR2,
type_entite IN VARCHAR2,
id_entite IN INTEGER,
code_entite IN VARCHAR2,
date_entite IN DATE,
error_message IN VARCHAR2,
sql_code IN INTEGER
) IS
BEGIN
INSERT INTO LOG(LOG_ID,LOG_DATE, LOG_PROCEDURE, LOG_TYPE_ENTITE, LOG_ID_ENTITE, LOG_CODE_ENTITE, LOG_DATE_ENTITE, LOG_SQLERRM, LOG_SQLCODE)
VALUES (SEQ_LOG.NEXTVAL,SYSDATE, nom_procedure, type_entite, id_entite, code_entite, date_entite, error_message, sql_code);
END;
PS:
I am passing liaison_code when calling the method this datatype:
LURE 3B.RE61
THANN3AMARI2
C.SAU3ZCRIM1
PYMON6VOUGL1
ARGIE3ARSOT1
NEUVY 3
ZNEUV 3 1
....
Thanks
I create a store procedure in Oracle db that insert Customer to my Table
this is my code :
CREATE OR REPLACE PROCEDURE THP.INSERT_CUSTOMER(
P_CUSTNAME IN VARCHAR2,
P_CUSTLAST IN VARCHAR2,
P_CUSTFATHER IN VARCHAR2,
P_NATIONNO IN NUMBER,
P_BIRTHDAY IN VARCHAR2,
P_BIRHTPLACE IN VARCHAR2,
P_EMAIL IN VARCHAR2,
P_CUSTENAME IN VARCHAR2,
P_CUSTELAST IN VARCHAR2,
P_OWNID IN NUMBER,
P_CUSTTYPEID IN NUMBER,
P_GENDERID IN NUMBER,
P_BILLSTID IN NUMBER,
P_BILLSPID IN NUMBER,
P_IDNO IN varchar2,
RESULT OUT INTEGER) IS
CNT NUMBER;
BEGIN
RESULT := 1;
CNT := 0;
SELECT COUNT(1) INTO CNT FROM THP.TBCUSTOMER WHERE NATIONNO=P_NATIONNO ;
IF CNT=1 THEN
COMMIT;
RESULT := 1; --IF RECORD is EXIST
ELSE
BEGIN
INSERT INTO TBCUSTOMER(CUSTID,CUSTNAME,CUSTLAST,CUSTFATHER,NATIONNO,BIRTHDAY,BIRHTPLACE,EMAIL,CUSTENAME,CUSTELAST,OWNID,CUSTTYPEID,GENDERID,BILLSTID,BILLSPID,IDNO)
VALUES(CUSTID_SEQ.NEXTVAL,P_CUSTNAME,P_CUSTLAST,P_CUSTFATHER,P_NATIONNO,P_BIRTHDAY,P_BIRHTPLACE,P_EMAIL,P_CUSTENAME,P_CUSTELAST,P_OWNID,P_CUSTTYPEID,P_GENDERID,P_BILLSTID,P_BILLSPID,P_IDNO);
COMMIT;
RESULT :=0; --IF INSERT NEW COLUMN
END;
END IF;
END INSERT_CUSTOMER;
/
now I want use this procedure in ESQL and create it directly in ESQL not CALL it from Oracle database or other DB
would you please guide me a bout it...
General comments, not an answer ...
count(1)
count(1) = count(*), which is the standard form for "count the number of rows". count(1) has no advantages, so best to use count(*).
RESULT := 1
is redundant at the beginning of the procedure
CNT := 0
... also redundant. The variable name is not very meaningful, and might make people think of a rude word, so perhaps change it to rows_found.
Prefixing the arguments with P_ is not required. If you use one of them in a SQL statement and need to deconflict it from a database object name then prefix it with the procedure name, so you have:
WHERE NATIONNO= INSERT_CUSTOMER.NATIONNO
Is NATIONNO constrained to be unique in the customer table? If not, use:
SELECT COUNT(*)
INTO CNT
FROM THP.TBCUSTOMER
WHERE NATIONNO=INSERT_CUSTOMER.NATIONNO AND
ROWNUM = 1;
(12g will introduce the LIMIT SQL syntax, by the way).
Commiting in a procedure is often held to be bad practice, as the procedure often becomes part of a longer business transaction (eg. inserting a new customer and address) and the commit should be controlled by the application code.
Upper case code is harder to read than lower case -- that's why direction signs on motorways are not uppercase.
The begin-end block for the insert is not required at all.
"birhtplace" is spelled wrong.
So I'd suggest that what you want to convert to ESQL is actually:
create or replace procedure thp.insert_customer(
custname in varchar2,
custlast in varchar2,
custfather in varchar2,
nationno in number ,
birthday in varchar2,
birhtplace in varchar2,
email in varchar2,
custename in varchar2,
custelast in varchar2,
ownid in number ,
custtypeid in number ,
genderid in number ,
billstid in number ,
billspid in number ,
idno in varchar2,
result out integer) is
rows_found number;
begin
select count(*)
into rows_found
from thp.tbcustomer
where nationno=insert_customer.nationno;
if rows_found = 1 then
result := 1;
else
insert into
tbcustomer(
custid ,
custname ,
custlast ,
custfather,
nationno ,
birthday ,
birthplace,
email ,
custename ,
custelast ,
ownid ,
custtypeid,
genderid ,
billstid ,
billspid ,
idno)
values(
custid_seq.nextval,
custname ,
custlast ,
custfather,
nationno ,
birthday ,
birthplace,
email ,
custename ,
custelast ,
ownid ,
custtypeid,
genderid ,
billstid ,
billspid ,
idno);
result :=0;
end if;
end insert_customer;
/
To call an external procedure via ESQL, you need to first declare the external function.
Do it as below:
CREATE PROCEDURE DoSomething (IN in1 CHARACTER, OUT out1 CHARACTER, OUT out2 CHARACTER)
LANGUAGE DATABASE
EXTERNAL NAME "DoSomething";
Now you can call this function from your ESQL function as below:
CALL DoSomething(in,out1,out2) IN Database.yourschema.yourDB;
P.S. The parameters you are passing must be compatible with the parameters of your stored procedure.
I need help. I have the following stored procedure but I have concurrency problems. Anybody can tell me how to modify the stored procedure to avoid duplicate key error because two users make the same selection at once?. The primary key is the field IDSOLICITUD and the table os SOLIC, that is numeric and I want to insert into this table a counter each time you access it and return that value to the calling application. The structure of the table I can not change, I can not put a AutoNumber.
CREATE procedure PRC_SOLIC(FECHA IN DATE, IDTRAMITE IN VARCHAR2, ESTADO IN NUMBER, ESTADO_FECHA IN DATE, MENSAJE_ERROR IN VARCHAR2, CPROVIN IN NUMBER, CMUNICI IN NUMBER, NHABITA IN NUMBER, NDOMICI IN NUMBER, REFORIGEN IN VARCHAR2,OPERACION_TIPO IN VARCHAR2, OPERACION_CODIGO IN NUMBER, USUARIO IN VARCHAR2, FINALIDAD IN VARCHAR2, RESULTADO IN VARCHAR2,NUM OUT NUMBER) is
v_Numero NUMBER;
CURSOR c_Solic is SELECT MAX(IDSOLICITUD)+1 FROM SOLIC;
BEGIN
OPEN c_Solic;
FETCH c_Solic INTO v_Numero;
IF (v_Numero is NULL) THEN
v_Numero := 1;
END IF;
INSERT INTO SOLIC VALUES (v_Numero, FECHA, IDTRAMITE, ESTADO, ESTADO_FECHA, MENSAJE_ERROR, CPROVIN, CMUNICI, NHABITA,
NDOMICI, REFORIGEN, OPERACION_TIPO, OPERACION_CODIGO, USUARIO, FINALIDAD, RESULTADO);
NUM := v_Numero;
close c_Solic;
END PRC_SOLIC;
Thank you.
Your tags are confusing, is it Oracle or MySQL? If Oracle then look into using sequences for this sort of thing. See Managing Sequences. You can then create a sequence such as:
CREATE SEQUENCE IDSOLICITUD_SEQ
START WITH 1
INCREMENT BY 1;
And then in your code:
CURSOR c_Solic is SELECT IDSOLICITUD_SEQ.NEXTVAL FROM SYS.DUAL;
edit based on being unable to use AutoIncrement
The following assumes you are using MySQL, for Oracle please see the other answer
Seeing as you can't change it to be an AutoIncrement you seem to have to resort to to explicitly locking the table using LOCK TABLES
http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
This gives
CREATE procedure PRC_SOLIC(FECHA IN DATE, IDTRAMITE IN VARCHAR2, ESTADO IN NUMBER, ESTADO_FECHA IN DATE, MENSAJE_ERROR IN VARCHAR2, CPROVIN IN NUMBER, CMUNICI IN NUMBER, NHABITA IN NUMBER, NDOMICI IN NUMBER, REFORIGEN IN VARCHAR2,OPERACION_TIPO IN VARCHAR2, OPERACION_CODIGO IN NUMBER, USUARIO IN VARCHAR2, FINALIDAD IN VARCHAR2, RESULTADO IN VARCHAR2,NUM OUT NUMBER) is
BEGIN
// Declare the variable which will hold the "AI" field
DECLARE thisSolicID INT UNSIGNED DEFAULT 0;
// Lock the table for writing
LOCK TABLES SOLIC WRITE;
// Get the "AI" value
SELECT COALESCE(MAX(IDSOLICITUD),0)+1 INTO thisSolicID FROM FROM SOLIC;
// Insert it
INSERT INTO SOLIC VALUES (thisSolicID, FECHA, IDTRAMITE, ESTADO, ESTADO_FECHA, MENSAJE_ERROR, CPROVIN, CMUNICI, NHABITA, NDOMICI, REFORIGEN, OPERACION_TIPO, OPERACION_CODIGO, USUARIO, FINALIDAD, RESULTADO);
// Unlock the table
UNLOCK TABLES;
END PRC_SOLIC;