Identity & Sequences
SQL Server's traditional approach bakes the counter directly into the column with
IDENTITY. Classic Oracle treats "generate the next primary key value" as a
separate concern from the table itself: you create a standalone SEQUENCE object
and pull from it explicitly. Both approaches now exist in both databases, but the
traditional-and-most-common pattern on each side is different enough to trip people up.
💡 The Bottom Line
SQL Server's classic pattern: mark the column itself IDENTITY(1,1) and just
omit it from your INSERT column list — the engine fills it in. Classic Oracle's
pattern: create a SEQUENCE, then reference sequence_name.NEXTVAL
explicitly in your INSERT. To retrieve the value you just generated, SQL Server
uses SCOPE_IDENTITY(); Oracle uses CURRVAL (or the
RETURNING clause).
Generating primary keys
CREATE TABLE employees (
employee_id INT IDENTITY(1,1) PRIMARY KEY,
first_name VARCHAR(50)
);
INSERT INTO employees (first_name)
VALUES ('Ada');
SELECT SCOPE_IDENTITY();
CREATE SEQUENCE emp_seq
START WITH 1
INCREMENT BY 1;
INSERT INTO employees (employee_id, first_name)
VALUES (emp_seq.NEXTVAL, 'Ada');
SELECT emp_seq.CURRVAL FROM DUAL;
📋 Key differences
IDENTITYis a property of one specific column on one specific table; it can't be shared. A classic OracleSEQUENCEis a free-standing object, decoupled from any table — you can share one sequence across several tables, or generate values without inserting anywhere.- With
IDENTITY, you never reference the counter in theINSERTat all — you just leave the column out of the column list and SQL Server assigns it.NEXTVALboth advances and returns the sequence's counter; in Oracle you call it inline inside theINSERTyourself, on every insert, or the row gets no value at all for that column. SCOPE_IDENTITY()reads the last identity value generated by an insert in the current scope (same stored procedure/batch). Oracle'sCURRVALreads the last value your session generated from a specific named sequence — closely analogous, but scoped to a named sequence object rather than to execution context.- SQL Server also has
@@IDENTITYandIDENT_CURRENT('table')for wider scopes — Oracle has no exact equivalents, sinceCURRVALis always tied to one specific sequence name rather than to "whatever table was last inserted into."
⚠️ Gotcha
Calling emp_seq.CURRVAL before you've called emp_seq.NEXTVAL at
least once in your current session raises ORA-08002: sequence NEXTVAL has not been
defined in this session. Unlike SQL Server's SCOPE_IDENTITY(), which simply
returns NULL if nothing has been inserted yet, Oracle's CURRVAL
throws a hard error the very first time — you always need at least one NEXTVAL
call in the session before CURRVAL means anything.
The modern bridge: Oracle 12c+ IDENTITY columns
CREATE TABLE employees (
employee_id INT IDENTITY(1,1) PRIMARY KEY,
first_name VARCHAR(50)
);
INSERT INTO employees (first_name)
VALUES ('Ada');
CREATE TABLE employees (
employee_id NUMBER GENERATED ALWAYS
AS IDENTITY,
first_name VARCHAR2(50)
);
INSERT INTO employees (first_name)
VALUES ('Ada');
If your target is Oracle 12c or later, you don't have to give up the IDENTITY
workflow you already know: GENERATED ALWAYS AS IDENTITY is a column-level
auto-increment that behaves almost exactly like SQL Server's IDENTITY — no separate
sequence object to manage, and you just omit the column from your INSERT. Under the
hood it's actually implemented using a hidden sequence, but you don't need to touch that sequence
directly; it's a genuine bridge concept between the two worlds.
🔧 Try It Yourself
Take a SQL Server table that uses IDENTITY(1,1) and rewrite it two ways for
Oracle: once using the classic pattern (a standalone CREATE SEQUENCE plus
referencing NEXTVAL in every INSERT), and once using Oracle 12c+'s
GENERATED ALWAYS AS IDENTITY. Notice how much closer the second version feels to
the SQL Server table you started with.
🙋 There Are No Dumb Questions
Q: If I'm stuck on an older Oracle version without 12c+ identity columns, how did
people handle auto-increment before that existed?
A: The classic pre-12c pattern pairs a SEQUENCE with a
BEFORE INSERT trigger on the table: the trigger calls
:NEW.employee_id := emp_seq.NEXTVAL; whenever the column is left null, so callers
don't have to reference the sequence explicitly in every INSERT — much closer to
the "just omit the column" experience you're used to from SQL Server. You'll see this
sequence-plus-trigger pattern constantly in existing Oracle codebases, even on databases that
are new enough to use the simpler 12c+ syntax instead.
✅ Quick Check
You've just run INSERT INTO employees (employee_id, first_name) VALUES (emp_seq.NEXTVAL, 'Ada');
for the first time in a brand-new session, and now want the ID that was just generated. What
happens if you call emp_seq.CURRVAL in that same session right after?
Next up: moving business logic across — how T-SQL's stored procedures and schemas compare to PL/SQL's block structure and packages.