简体   繁体   中英

Oracle Sequence Value Increments when Unique Constraint Violated

I created an Oracle sequence and trigger to auto-increment the primary key column on a table when new records are inserted. Here is my code:

CREATE TABLE MOBILE_APP
(
  "MOBILE_APP_ID" NUMBER(9, 0) PRIMARY KEY,
  "NAME" VARCHAR2(60) NOT NULL,
  "DESCRIPTION" VARCHAR2(200),
  CONSTRAINT name_unique UNIQUE (name)
);

CREATE SEQUENCE MOBILE_APP_ID_SEQ
MINVALUE 1
MAXVALUE 999999999
INCREMENT BY 1
START WITH 1
NOCACHE
ORDER
NOCYCLE;

CREATE TRIGGER MOBILE_APP_BR_I
BEFORE INSERT ON MOBILE_APP
FOR EACH ROW
BEGIN
  SELECT MOBILE_APP_ID_SEQ.NEXTVAL INTO :NEW.MOBILE_APP_ID FROM dual;
END;

Since my trigger is "before insert," it will execute before a record is actually inserted into the table. But I did not expect my trigger to execute even in the case of a unique constraint violation during an insert. Let's say that the table, sequence and trigger are all new, and I try executing the statement below twice.

INSERT INTO MOBILE_APP (name, description) VALUES ('Name', 'Desc');

The first execution will complete successfully, auto-populating the value of 1 in the "mobile_app_id" field for the inserted record. As expected, the second execution will error out with a unique constraint violation relative to the "name" field. But if I then insert another record without violating the unique constraint, the value 3 is auto-populated in the "mobile_app_id" field for the inserted record -- meaning that during the attempted insert that failed due to a unique constraint violation, the value of the sequence still incremented from 1 to 2. How can I prevent this? I found this other post , but unfortunately it contains no solution to the problem. Any help will be greatly appreciated!

This is normal behavior for sequences. You are not guaranteed to have a gapless sequence. Because multiple transactions could all be running, and some get committed and some rolled back, I'm not even sure how you could have a sequence that gave you a gapless sequence like it sounds like you want.

If you really do need a gapless sequence like that, you'll have to ensure it's incremented in the same transaction:

CREATE TABLE my_id ( col1 NUMBER );
INSERT INTO my_id ( col1 ) VALUES ( 0 );

Then perform your transaction:

DECLARE
    id NUMBER;
BEGIN
  UPDATE my_id SET col1 = col1 + 1
  RETURNING col1 INTO id;

  INSERT INTO mobile_app ... whatever ...

  COMMIT;
END;

The big problem with this is that the id table becomes a bottleneck. You can't insert into the mobile_app table from different sessions concurrently.

I really appreciate all of the comments and answers provided! Based on the information provided, I think the best approach is to just leave things as-is and expect that the sequence might not remain gapless. There appear to be too many potential disadvantages to proceeding in any other fashion.

  1. You can just insert the sequence number within the insert statement.

    INSERT INTO MOBILE_APP (MOBILE_APP_ID,name, description) VALUES (MOBILE_APP_ID_SEQ.NEXTVAL ,'Name', 'Desc');

  2. If you use another application do this insertion, get the id first then pass into insert statment and other process.

      --Create Id. CREATE OR REPLACE PROCEDURE MOBILE_APP_ID (MOBILE_APP_ID OUT SYS_REFCURSOR ) IS BEGIN open MOBILE_APP_ID for SELECT MOBILE_APP_ID_SEQ.NEXTVAL MOBILE_APP_ID FROM dual; END ; --Then get the id. int id = MOBILE_APP_ID.MOBILE_APP_ID; --Then insert the id. INSERT INTO MOBILE_APP (MOBILE_APP_ID,name, description) VALUES (id ,'Name', 'Desc'); 

As already mention sequences are sequences and not suitable if you need a gapless sequence. First think is are you sure that you should need a gapless sequence? If so neither of the suggested solutions is 100% safe of gaps. Both will have problems if another session gets the id prior to that the first session commits its changes. The second session will then have the same id as the first and that will of course cause problems. The risk for this shouldn't be that much but it is still there specially if you have many active sessions.

Not easy to get a fully gapless sequence. One way could be eg to use something like the suggested table solution and then trap any duplicate key errors and then retry to you get it inserted. Not nice and not efficient but it should get the task done.

I think U dnt have need to Make any Sequence here or any thing else except the Below Code

CREATE OR REPLACE TRIGGER AUTO_APPID
BEFORE INSERT
ON MOBILE_APP
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
begin

SELECT NVL(MAX(MOBILE_APP_ID),0)+1 INTO :NEW.MOBILE_APP_ID FROM MOBILE_APP;

END ;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM