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;
Related
I have two tables, vehicles and distance, and would like to show luggage _capacity and seating_capacity when I execute procedure with source_town, source_state, dest_town, and dest_state. Distances table have foreign key ( vehicles_id).
this is what I have so far
create or replace procedure availabe_ride (
s_town varchar2,
s_state varchar2,
d_town varchar2,
d_state varchar2,
l_capacity number,
s_capacity number)
is
begin
select source_town, source_state, dest_town, dest_state, luggage_capacity, seating_capacity
into s_town, s_state, d_town, d_state, l_capacity, s_capacity
from vehicles v, distances d
where v.vehicles_id = d.vehicles_id;
I can't fogure out how to do this...
please help...
FYI: Oracle 12c
I have created a custom type called Payeezy_Error:
create or replace TYPE PAYEEZY_ERROR
AS
OBJECT (
CODE VARCHAR(30),
DESCRIPTION VARCHAR(200)
);
And then in turn created a Table type of Payeezy_Errors:
create or replace TYPE PAYEEZY_ERRORS AS TABLE OF PAYEEZY_ERROR;
I then have a Procedure that takes Payeezy_Errors as an IN parameter:
create or replace PROCEDURE SAVE_USER_PAYMENT_TRANSACTION
(
in_AccountID IN VARCHAR2,
in_SequenceID IN VARCHAR2,
in_CorrelationID IN VARCHAR2,
in_TransactionID IN VARCHAR2,
in_TransactionTag IN VARCHAR2,
in_Currency IN VARCHAR2,
in_TransactionType IN VARCHAR2,
in_BankResponse IN VARCHAR2,
in_GatewayResponse IN VARCHAR2,
in_ValidationStatus IN VARCHAR2,
in_TransactionStatus IN VARCHAR2,
in_Errors IN PAYEEZY_ERRORS
)
AS
var_uptID NUMBER;
var_ErrorCount NUMBER := 0;
EX_AUTHENTICATION EXCEPTION;
BEGIN
-- Insert the Payeezy Response values tied to the user
INSERT INTO
USER_PAYMENT_TRANSACTION (
ACCOUNT_ID, UP_PAYMENT_SEQ_ID, CORRELATION_ID, TRANSACTION_ID,
TRANSACTION_TAG, CURRENCY, TRANSACTION_TYPE, BANK_RESPONSE,
GATEWAY_RESPONSE, VALIDATION_STATUS, TRANSACTION_STATUS
) VALUES (
in_AccountID, in_SequenceID, in_CorrelationID, in_TransactionID,
in_TransactionTag, in_Currency, in_TransactionType, in_BankResponse,
in_GatewayResponse, in_ValidationStatus, in_TransactionStatus
)
RETURNING
ID
INTO
var_uptID;
-- Insert any errors that may be associated with a failure/unsuccessful transaction
SELECT
COUNT(*)
INTO
var_ErrorCount
FROM
in_Errors;
IF (var_ErrorCount > 0) THEN
INSERT INTO
USER_PAYMENT_TRANSACTION_ERROR (
UPT_ID, CODE, DESCRIPTION
)
SELECT
var_uptID, e.CODE, e.DESCRIPTION
FROM
in_Errors;
END IF;
-- Exception Handling
EXCEPTION
WHEN EX_AUTHENTICATION THEN
raise_application_error(-20001, 'Authentication Failed.');
END SAVE_USER_PAYMENT_TRANSACTION;
When I compile the procedure it yells at me at the SELECT COUNT(*) statement that:
ORA-00942: table or view does not exist.
And red-dashes are under SELECT and in_Errors.
Further down the procedure I get the same error and the second INSERT INTO and in_Errors lines are red-dashes too.
I have exited and reloaded Oracle SQL Developer just to see if it was a caching thing. I have searched around the web but have not found my particular case.
If you want to use the table in a query, you'd need to use the table operator
SELECT
COUNT(*)
INTO
var_ErrorCount
FROM
table( in_Errors );
That works. But it means that you're taking all of the data that you have in the PL/SQL collection, moving it to the SQL VM, doing the aggregation, and then returning the result to PL/SQL. It would likely be more efficient (and less code) in this case to just do
var_ErrorCount := in_Errors.count;
I need to insert into a table 500.000 records, so the process will take a while. I read about the indexes, that increase the speed of a query, but the question is next: on which parameter should I set the index in order to make the INSERT faster? (the id?).
Thanks.
Edited
CREATE OR REPLACE PROCEDURE addNewUser(
firstName IN VARCHAR2,
lastName IN VARCHAR2,
email IN VARCHAR2,
username IN VARCHAR2,
password IN VARCHAR2,
job IN VARCHAR2) AS
v_usersCount NUMBER := 0;
v_userID NUMBER := 0;
BEGIN
SELECT COUNT(*) INTO v_usersCount
FROM Users;
v_userID := v_usersCount + 1;
INSERT INTO Users VALUES(v_userID,firstName,lastName,email,username,password,job);
END addNewUser;
and the data is added like this :
FOR i IN 1..50000
LOOP
addNewTask('Task 1', 'Just Starter', 'High', 1, 'This is a description task.', 'Task Comment', '20-08-2015', 1);
END LOOP;
You are doing a query for each insertion, which makes it slow.
Try calculate the ID outside your stored and pass it to the procedure, so you don't have to query it in your procedure every time.
SELECT COUNT(*)... is one of slowest things you can do in any program. This requires reading every row in the table - so if you're inserting 1000 rows the first time it reads zero rows (fast), next time 1 row (fast), next time 2 rows...and eventually you've executed 1000 queries and read 499500 rows (sum of 0 through 999).
In this case you should use a SEQUENCE to generate non-repeating numbers. I'd rewrite your code as:
CREATE SEQUENCE VALUE_SEQ; -- just take defaults for all parameters
CREATE OR REPLACE PROCEDURE addNewUser(
firstName IN VARCHAR2,
lastName IN VARCHAR2,
email IN VARCHAR2,
username IN VARCHAR2,
password IN VARCHAR2,
job IN VARCHAR2) AS
BEGIN
INSERT INTO Users
VALUES(VALUE_SEQ.NEXTVAL, firstName,lastName,
email,username,password,job);
END addNewUser;
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 have a table non_employee with emp_no as the primary key and a package with a procedure to insert in to this table.
I need to be able to autoincrement the emp_no when the procedure is run. I tried creating a sequence within the procedure like this, but getting errors. please see below and advice.
CREATE OR REPLACE PACKAGE BODY WFDDEV."ERD" IS
create SEQUENCE #seq_emp_nmbr;
PROCEDURE INS_NON_EMPLOYEES
(
in_DATE_ADDED DATE,
in_EMPLOYEE_NAME VARCHAR2,
in_DEPT_ID VARCHAR2,
in_SUB_DEPARTMENT VARCHAR2,
in_LOCATION VARCHAR2,
in_WORK_TEL_NO VARCHAR2,
in_TOUR VARCHAR2,
in_REST_DAYS VARCHAR2,
in_HOME_ADDRESS VARCHAR2,
in_CITY VARCHAR2,
in_STATE VARCHAR2,
in_ZIP VARCHAR2,
in_HOME_TEL_NO VARCHAR2,
in_GENDER VARCHAR2,
in_RACE VARCHAR2,
in_DATE_OF_BIRTH DATE,
in_AGE VARCHAR2,
in_HIRE_DATE DATE,
in_UNION_AFFILIATION VARCHAR2,
in_TITLE VARCHAR2,
in_NON_EE_INDICATOR VARCHAR2
) IS
BEGIN
INSERT INTO WFDDEV.NON_EMPLOYEES
(
EMP_NO,
DATE_ADDED,
EMPLOYEE_NAME,
DEPT_ID,
SUB_DEPARTMENT,
LOCATION,
WORK_TEL_NO,
TOUR,
REST_DAYS,
HOME_ADDRESS,
CITY,
STATE,
ZIP,
HOME_TEL_NO,
GENDER,
RACE,
DATE_OF_BIRTH,
AGE,
HIRE_DATE,
UNION_AFFILIATION,
TITLE,
NON_EE_INDICATOR
)
VALUES
(
emp_no.NEXTVAL,
in_DATE_ADDED,
in_EMPLOYEE_NAME,
in_DEPT_ID,
in_SUB_DEPARTMENT,
in_LOCATION,
in_WORK_TEL_NO,
in_TOUR,
in_REST_DAYS,
in_HOME_ADDRESS,
in_CITY,
in_STATE,
in_ZIP,
in_HOME_TEL_NO,
in_GENDER,
in_RACE,
in_DATE_OF_BIRTH,
in_AGE,
in_HIRE_DATE,
in_UNION_AFFILIATION,
in_TITLE,
in_NON_EE_INDICATOR
);
END;
Im getting PLS-00103: Encountered the symbol "CREATE" when expecting one of the following:
begin end function pragma procedure subtype type
error with this...
You need to create the sequence just once outside of the package as a separate database object. Then, in the insert statement in your package body you can reference the sequence to get the next value.
Try-
EXECUTE IMMEDIATE 'CREATE SEQUENCE SEQ_NAME START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 1000000 NOCYCLE NOCACHE ORDER';
This should be inside a Procedure or Function body and not in the Declaration section i.e. this should be treated as a executable statement.
Creating a sequence using Dynamic SQL is a bad idea and I am not sure why you would want to do that. However, if you are creating a sequence dynamically then remember to drop it as well after finishing up, using
EXECUTE IMMEDIATE 'DROP SEQUENCE SEQ_NAME';
This way you would atleast not run into errors (like ORA-00955) while calling the package procedure.