How to Create Auto Increment Columns in Oracle
Categories: Database Administration, Information Technology, Oracle
After pointing out how not to create auto increment columns in Oracle, I suppose I should point out how to create auto increment columns in oracle.
Many will gripe about this not being a standard feature in Oracle, but when it’s as easy as two more commands after your CREATE TABLE command I can’t see any good reason to use fancy SQL on every insert.
First let’s create a simple table to play with.
SQL> CREATE TABLE test
(id NUMBER PRIMARY KEY,
name VARCHAR2(30));
Table created.
Now we’ll assume we want ID to be an auto increment field. First we need a sequence to grab values from.
SQL> CREATE SEQUENCE test_sequence
START WITH 1
INCREMENT BY 1;
Sequence created.
Now we can use that sequence in an BEFORE INSERT trigger on the table.
CREATE OR REPLACE TRIGGER test_trigger
BEFORE INSERT
ON test
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT test_sequence.nextval INTO :NEW.ID FROM dual;
END;
/
Trigger created.
This trigger will automatically grab the next value from the sequence we just created and substitute it into the ID column before the insert is completed.
Now we’ll do some inserts:
SQL> INSERT INTO test (name) VALUES ('Jon');
1 row created.
SQL> INSERT INTO test (name) VALUES ('Bork');
1 row created.
SQL> INSERT INTO test (name) VALUES ('Matt');
1 row created.
SQL> SELECT * FROM test;
ID NAME
---------- ------------------------------
1 Jon
2 Bork
3 Matt
Because the sequence is updated independent of the rows being committed there will be no conflict if multiple users are inserting into the same table simultaneously.
If you need to capture the value of the auto increment column you’ll want to check out my other article Oracle Auto increment Columns – Part 2
Matt has posted a similar method in which he uses a sequence in the insert eliminating the need for the trigger. That will work just as well without the need for the trigger. The only drawback to this method is slightly longer SQL commands.
55 Responses to “How to Create Auto Increment Columns in Oracle”
-
Life After Coffee » How Not to Create Auto Increment Columns in Oracle Says:
February 20th, 2006 at 10:04 am[...] This article is about how not to create auto increment columns. If you’re looking for a more acceptable way, you’ll want to check out this article. [...]
-
Fabio Xodo Says:
March 2nd, 2006 at 4:55 amThere is an issue caused by using directly the increment of a sequence in an insert statement
insert into table (id) values (seq.nextval)
is okay, but will not make inserts work in the table in some case.
For example, I had to use and MS Access interface to insert rows in an oracle db. Using the statement above will not work.You’ll have to use the trigger one behaivor, demanding the sequence increment directy to the oracle server.
Hope this helps
-
Tony Jackson Says:
March 21st, 2006 at 8:50 pm1. Works great, thanks for posting this.
2. These two lines were not needed:DECLARE
seqNo NUMBER;I removed them and the trigger still works correctly.
Please let me know if they are really needed (ie, is this going to bite me later?)
3. I don’t understand the line:
REFERENCING NEW AS NEW OLD AS OLDso if you have any explanation, I would be greatful.
Thanks again,
Tony Jackson
atjsfo@hotmail.com -
Jon Says:
March 21st, 2006 at 9:18 pmTony, glad this helped you.
The DECLARE portion must be left from some previous revision of some other trigger. It’s not necessary and I will be removing it form the code above. Thanks for pointing this out.
The REFERENCING NEW AS NEW OLD AS OLD gives you the :NEW and :OLD objects so you can refer to columns as :NEW.id or :NEW.name. If it’s an insert there is really just a :NEW, but for an update you would have the new values in :NEW and the old values in :OLD.
Hope that all makes sense. Let me know if you’re looking for more.
-
Jon Says:
March 21st, 2006 at 10:00 pmUpdate: Thanks to Tony for pointing out a couple things that could be tightened up.
I left out the OLD AS OLD since there is no OLD data on inserts. The NEW AS NEW could also be excluded, but it’s nice to be explicit here. I also took out the DECLARE line where I declared a variable I never used.
-
Narender Says:
March 22nd, 2006 at 4:49 amthank u very much for giving to this information.
-
Jon Says:
March 22nd, 2006 at 8:10 amYou’re welcome.
Glad I could help.
Jon
-
Daniel Says:
May 5th, 2006 at 7:17 pmGreat post!
Sometimes I do this in PL/SQL:
nextID := test_sequence.nextval;
insert into test (ID,name) values (nextID,’bzzt’);
…use nextID for other workI’d like to use your trigger solution, but was wondering about the best way to capture the most recent ID.
Any thoughts?
Thank you!
-
Shaun McDonald Says:
May 10th, 2006 at 6:12 amI’m doing a java program that interfaces with an oracle database and require to know how to get the id generated for the last insert for use in later processing.
Any thoughts would be great.
Shaun
-
Megha Says:
May 12th, 2006 at 7:07 amThanks For this Post! this has made the oracle world easy!
-
Zach Says:
May 22nd, 2006 at 1:42 pmAny clue how I can easily and reliably grab that id number in PHP? In PHP with MySQL I would use the function mysql_insert_id() to grab it. However, there is obviously no direct equivelant in Oracle I’m aware of. Thoughts?
-
Life After Coffee » Oracle Auto increment Columns - Part 2 Says:
May 22nd, 2006 at 5:20 pm[...] Three separate people have commented on my previous article on How to Create Auto Increment Columns in Oracle asking how they can retrieve the value of an auto increment column for use later in their code. Well Daniel, Shaun and Zach, here’s the answer. [...]
-
D Lee Says:
May 26th, 2006 at 9:29 amI copied your codes (create table, sequence and trigger). The first two (table and sequence) worked, but the creating of trigger failed. The error message is “trigger created with PL/SQL compilation error”. And could insert any vales to tables due to fault trigger. Any hints? By the way, I used MS_Query to execute those SQL statements.
-
Jon Says:
May 26th, 2006 at 10:31 amD, what version of Oracle are you using? I’m not familiar with MS_Query, but some apps don’t like having a semicolon at the end of a PL/SQL block (before the forward slash).
Try again and then run the command
show errorsand that should give you some better error messages. Good luck!Jon
-
D Lee Says:
May 26th, 2006 at 1:46 pmThanks for your reply.
The version of Oracle is 8.1
I tried to create the trigger without simicolon but it still doesn’t work. I tried “show errors”, but was told “Invalid SQL statement”. Because of that invalid trigger, I couldn’t insert any information into the table. Any more hints? Thanks.
-
Jon Says:
May 26th, 2006 at 3:25 pmTry it through SQL*Plus if possible. At least then the ’show errors’ command should work.
I would think this would work just fine in 8.1 but its been a while since I worked with anything prior to 9i.
Jon
-
Keyur Says:
June 6th, 2006 at 3:21 pmI’m getting the following error when I try the above trigger with my table.
java.sql.SQLException: ORA-04098: trigger ‘CCTP_WDB.PROD_TRIGGER’ is invalid and failed re-validationProd_trigger is the name of my trigger. The rest is the same.
I’m using JSP to run the query and to connect to the database. PLease help. -
Jon Says:
June 6th, 2006 at 9:13 pmKeyur,
Try running the trigger creation in SQLPlus or SQL Developer. If the trigger still gives that error type ’show errors’ and you should get a more meaningful error message.
It sounds likely that the sequence didn’t create properly.
Jon
-
michael Says:
June 19th, 2006 at 5:03 pmHey, thanks for the post. I’m getting “PLS-00049: bad bind variable ‘NEW.ID’” when I run these create statements in SQL*Plus.
Am using ‘REFERENCING NEW AS NEW’ and ‘SELECT… INTO :NEW.ID FROM DUAL’.
Any ideas? Thanks!
michael -
michael Says:
June 19th, 2006 at 6:19 pmNever mind. Dumb mistake. It’s working fine.
-
Jon Says:
June 19th, 2006 at 6:51 pmGlad you got it working! Thanks for letting me know.
Jon
-
Md Khaliq Says:
July 4th, 2006 at 8:28 amThis is very useful to the developers who need help .
-
Stuart Says:
July 17th, 2006 at 10:35 amI had the same problem as Keyur. I was attempting to create my triggers using Squirrel (which I have to say is an excellent program, but not particularly good for proceedures). I found that Squirrel couldn’t interpret the commands, and was therefore creating invalid triggers, and wouldn’t tell me why. The Show errors command didn’t work. So I downloaded another excellent program called TOAD, which allowed me to create the triggers and get everything working. It has an excellent interface with SQL window and proceedure window. It also has a wizard type feature which helps you create triggers or a series of other statements if you are a novice or unsure of the syntax.
I thought other users/readers might find this information useful.
Stu
-
Jon Says:
July 17th, 2006 at 10:43 amStuart,
Glad TOAD worked for you. We used to use it heavily for development but have recently switched to Oracle’s SQL Developer. Check it out. It has much of the functionality of TOAD with less clutter.
Thanks for sharing.
-
Michael Says:
July 21st, 2006 at 1:50 pmHi Jon, thank you for taking the time to address sequencing. I have a variation of the code above that I’m trying to write. It would dynamically identify the number by which I have to ALTER the incremental value of a SEQUENCER in Oracle. My scenario is that we have an external batch load of data into the data warehouse that is not using the sequencer.. The SEQ field is manually being set to begin at the max+1 of the value in the table. So every night I have to reset the sequencer to be the max value of that same table to account for the recently added records.
So I’ve got all the pieces I need, but when I put them together inside a stored procedure I get a syntax error with no explanation. Here’s the code. I must be missing something really simple that I hope you will see easily. If you have a moment, I’d appreciate your insight. Thanks. Mike
/* I just keep this code here for safe keeping – ignore for now
create table MGA_AUDIT_SEQ (
MGA_POLICY_MAXVAL NUMBER
, MAX_POL_SEQ_NEXTVAL NUMBER — THIS IS THE VALUE IN CACHE FOR THE SEQUENCER
, SEQ_DIFF NUMBER — THIS IS THE DIFFERENCE BETWEEN THE 2 ABOVE VALUES
, LOAD_DATE VARCHAR2(10) — JUST SYSDATE IN MM/DD/YYYY FORMAT
);
COMMIT;
*/create or replace procedure sp_alter_mga_pol_seq
is
newseq number;
nextseq number;
mgapolval number;
beginSELECT MAX(SEQ_ID) INTO MGAPOLVAL from mga_policy;
SELECT MGA_POL_SEQ.NEXTVAL INTO NEXTSEQ FROM DUAL;
INSERT INTO MGA_AUDIT_SEQ
VALUES (MGAPOLVAL,
NEXTSEQ,
NEXTSEQ-MGAPOLVAL,
TO_CHAR(SYSDATE, ‘MM/DD/YYYY’));–SEQ_DIFF is a value I have determined in the temp table I
–created aboveselect mga_pol_seq.currval + seq_diff +1
into newseq
from dual, mga_audit_seq;— set the sequencer to NOCACHE
ALTER SEQUENCE MGA_POL_SEQ
NOCACHE;
COMMIT;— reset the sequencing number to a new number
— which is now set in the variable NEWSEQALTER SEQUENCE MGA_POL_SEQ
INCREMENT BY newseq ; –
COMMIT;— HAVE TO CALL THIS HERE IN ORDER TO MAKE THE CHANGE AFFECTIVE
select mga_pol_seq.nextval from dual;
— bring the sequencer parameters back to their original state
ALTER SEQUENCE MGA_POL_SEQ
CACHE 20
nominvalue
increment by 1 ;
COMMIT;
end; -
David O'Reilly Says:
July 24th, 2006 at 6:22 amHey there
GREAT SITE! Im moving over from MySQL so this site is extremely helpful for me.
Just one question, I was thinking of creating a sequence that I could use on lots of other tables, so say:
CREATE SEQUENCE auto_increment
START WITH 1
INCREMENT BY 1;Then later for all my tables I would just use that sequence called auto_increment, however my question is as follows:
Would using the same sequence for all my other tables that need to be incremented be effected by other uses of it. As in say another table was using that sequence, would it effect my current table who is also using that same sequence? or would it be best to create a special sequence for every table that needs one.
Thanks
Dave -
David O'Reilly Says:
July 24th, 2006 at 6:38 amHopefully you might know what has happened to me here, I am getting a compilation error:
I created a table, created a sequence and then created the trigger in exactly the same way as your site describes, but it failed with the following errors:
Warning: Trigger created with compilation errors.
so I ran:
SQL> show errors trigger trig_users_auto_increment
Errors for TRIGGER TRIG_USERS_AUTO_INCREMENT:LINE/COL ERROR
——– —————————————————————–
2/46 PLS-00049: bad bind variable ‘NEW.ID’——-
Any ideas why it doesn’t like the variable new?
Here is a history of what I created:
CREATE TABLE users (
userID NUMBER PRIMARY KEY,
email VARCHAR2(100),
fname VARCHAR2(50),
sname VARCHAR2(50)
)——– AUTO_INCREMENT
CREATE SEQUENCE seq_users_auto_increment
START WITH 1
INCREMENT BY 1;——– trigger
CREATE OR REPLACE TRIGGER trig_users_auto_increment
BEFORE INSERT
ON users
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT seq_users_auto_increment.nextval INTO :NEW.ID FROM dual;
END;Thanks in advance for your help!!!!
Hugs
David
Dublin Ireland -
David O'Reilly Says:
July 24th, 2006 at 6:40 amops… I had a blonde moment! the reason it didn’t work was ID was not in my users table, it is userID not ID
Well I hope I helped something out there who may have had that highlighted moment
-
Jon Says:
July 24th, 2006 at 8:59 pmMichael,
You should try running this in SQL*Plus and after you get “procedure
compiled with compilation errors” or an error like this (I’m guessing
that’s what you’re getting) typeshow.
errorsThat should give you a more useful ORA- error. I’m guessing the
problem you’re having is with theALTER SEQUENCEcommand.
That is DDL (data description language), language that defines or
changes the objects and structures in the database. PL/SQL has
trouble with DDL and mostALTER,DROPand
CREATEcommands will result in an error when they are in
PL/SQL blocks.You might need to use an
EXECUTE IMMEDIATEcommand to make
this happen.Hope this helps!
-
Life After Coffee » Happy Birthday Life After Coffee! Says:
August 2nd, 2006 at 3:04 pm[...] How to Create Auto Increment Columns in Oracle [...]
-
Stephane Says:
August 4th, 2006 at 7:51 pmThank you very much for this clarification. It has been of great help. There is none the less one thing that I do not understand (althought your code works perfectly in my DB) is the DUAL keyword. What is its purpose?
Thank you very much.
-
Life After Coffee » 2006 » August » 16 Says:
August 16th, 2006 at 11:55 am[...] How to Create Auto Increment Columns in Oracle (178) [...]
-
Michael Knowles Says:
August 23rd, 2006 at 9:49 amhttp://www.adp-gmbh.ch/ora/misc/dual.html
Found this explanation of dual.
dual is a table which is created by oracle along with the data dictionary. It consists of exactly one column whose name is dummy and one record. The value of that record is X.
rene@ora92> desc dual
Name Null? Type
———————– ——– —————-
DUMMY VARCHAR2(1)rene@ora92> select * from dual;
D
-
X
The owner of dual is SYS but dual can be accessed by every user. As dual contains exactly one row (unless someone fiddled with it), it is guaranteed to return exactly one row in select statements if a constant expression is selected against dual, such as in:
select sysdate from dual
Although it is possible to delete the one record, or inse -
Nadeem Says:
August 28th, 2006 at 8:06 amHi,
Do you think there will be any issues with using a sequence and triggers in an OLTP as there involves a context switch when executing a trigger in the database?. In my approach I use my ORM layer (TROQUE) to generate the id, and it has served me well.
thanks
-
Jon Says:
August 29th, 2006 at 8:46 pmNadeem,
There should not be an issue if a sequence is used. If you grab nextval from a sequence and some one else grabs one even before you have committed your changes they will receive the next number in the sequence. Even if you do not use the value you retrieve from the sequence it is yours forever.
I’m not familiar with TROQUE, but if you’re worried you should probably ask their support about this?
Hope this helps!
-
Cirne.org · Como inserir um registo com “auto increment” id Says:
October 14th, 2006 at 7:34 am[...] Graças ao Life After Coffee por este post jeitoso… [...]
-
Sam Stephens Says:
October 25th, 2006 at 12:45 amThe trigger method guarantees that you can never have a duplicate primary key.
Using the sequence in the insert statement method works, until someone does an insert without using the sequence.
Lets say you have one row in your table, with ID = 1. Someone decides they need to have a record with ID = 10, and so they insert usinginsert into table (id) values (10). You now have a ticking timebomb – the sequence will continue to increment 2, 3 etc, until it reaches 10 at which time the insert fails with
ERROR at line 1:
ORA-00001: unique constraint (SYS.SYS_C002948) violated
However using the trigger method, if someone tries to specify an explicit ID value, the trigger will overwrite the specified value with the value from the sequence.
I guess there is some performance gain from not using the trigger, so if you trust all the code that accesses your database and performance is critical, the sequence in the insert statement method may be what you need. Otherwise, the trigger method is safest, and also means you can be lazy when inserting. -
Jon Emmons Says:
October 25th, 2006 at 8:11 amAll good points Sam. From a data architecture standpoint my tendency is to not trust the developers, especially in big organizations. Either is valid, just one tends to be riskier than the other.
-
syam sundar.Kota Says:
November 6th, 2006 at 2:07 amHi,
let me know how can get the sum of first and second sal ,second and third sal ,third and fourth sal and so on.. from emp table.please give me the SQL statement for this query -
neethu Says:
January 18th, 2007 at 7:34 amgr8..itz wrking properly
-
rod Says:
August 30th, 2007 at 11:36 pm“Many will gripe about this not being a standard feature in Oracle”
I am reading this when I should be onto my next task.
GRIPE…GRIPE…GRIPE -
Srinivas Paila Says:
December 4th, 2007 at 11:03 am
Hello Jon, Greetings!!! Great post.. This really helped me to resolve one of my critical requirements… Thank you so much… Cheers @Srinivas
-
Alexander Hartmaier Says:
January 30th, 2008 at 12:12 pmI’d recommend to define the trigger as:
CREATE OR REPLACE TRIGGER trig_test
BEFORE INSERT ON test
FOR EACH ROW
WHEN(new.id IS NULL)
BEGIN
SELECT seq_test.nextval INTO :new.id FROM dual;
END;This way you are able to specify the primary key (column ‘id’ in my example) if you need to.
-
Mohammed Says:
March 10th, 2008 at 8:10 amHi,
I am using sage crm in the frontend and oracle in the backend, we are using sage crm to log cases from the customer, whenever a new case is created, a ref id is generated by using the procedure and this works fine.
The problem occurs only when i create a new case and cancel it, ie; i am not saving the case, but when i create a new case again, i get a new ref id instead of previous ref id. This is destroying my report.
If i create a new case and cancel it then my ref id should be free and must be fetched for the next new case, how can i resolve this problem.
help greatly appriciated……
TIA
Mohammed. -
Neil Emms Says:
April 18th, 2008 at 8:38 amJust a minor suggestion. This does not work the same as the MySQL solution (which allows you to specify a value for ID if you want to), as the trigger will always add the value from the sequence.
If you wish to replicate that behaviour just change the line:
SELECT test_sequence.nextval INTO :NEW.ID FROM dual;
to be:
IF :NEW.ID is NULL THEN
SELECT test_sequence.nextval INTO :NEW.ID FROM dual;
END IF;This will now only use the sequence if no value is passed in for ID.
-
Kalpesh Suryawanshi Says:
August 8th, 2008 at 5:46 amHey, this article is really helpful.
-
S.Anugraha Says:
August 25th, 2008 at 4:49 amhey,wot do i do, if i need to generate a number sequence with character prefixes,say I need to auto increment the column ‘Product_id’ for every product inserted??
-
Jon Emmons Says:
August 25th, 2008 at 10:08 pmS.
If you want to have the same character appear before each sequence number all you need to do is concatenate the letter in the trigger like this:
SELECT ‘A’ || test_sequence.nextval INTO :NEW.ID FROM dual;
If you’re looking to increment the leading letter you’re going to have to get a bit trickier. Maybe others will have ideas, but something like a switch may be in order.
Hope this helps.
-
Vara Prasad Says:
February 16th, 2009 at 11:21 pmgood idea…………. Thank u.
-
keepitmassive Says:
March 9th, 2009 at 4:47 amThanks for a great article, that helped me a lot!
-
DebiPrasad Says:
April 29th, 2009 at 3:09 pmin the trigger code whats the meaning of that “dual” word
-
mona Says:
June 4th, 2009 at 1:50 pmthis code works perfectly right up until i delete a row from the table and start adding new rows.. the sequence starts jumping numbers.. help! any suggestions would be appreciated!
-
rainer Says:
September 30th, 2009 at 10:42 amthis code is just i’ve searched for, thanks.
I need help in getting the generated value using java:CREATE TABLE person (ID NUMBER(11) NOT NULL, givenName NVARCHAR2(60) default ‘A default value’ NOT NULL, surName NVARCHAR2(60) NOT NULL, CONSTRAINT person_pk PRIMARY KEY ( ID ) ) ORGANIZATION INDEX;
String sql = "insert into person (givenName,surName) values (?,?)";
PreparedStatement st = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
st.setString(1,"me");
st.setString(2,"fromMars");
st.executeUpdate();
ResultSet rs = st.getGeneratedKeys();
if (rs.next()) {
Object obj = rs.getObject();
}
The returned Object is a ROWID, and i’m not able to get the ID for the inserted row.
-
rainer Says:
October 1st, 2009 at 8:19 am… found a working solution:
String generatedColumns[] = {"ID"};
PreparedStatement st = conn.prepareStatement(sql, generatedColumns);
…
if (st.executeUpdate() != 0)
{
ResultSet rs = st.getGeneratedKeys();
if (rs.next())
int idValue = rs.getInt(1);
}
-
Phil Says:
October 1st, 2009 at 3:16 pmThis is fantastic. Thanks for making it so simple!

