Identity columns

advertisement
Oracle 12c Identity columns
(Corrado Piola)
Identity columns
- A new entry in Oracle® Database 12c -
Author: Corrado Piola, OCP
Last updated: September 2013
Introduction
Good news for software and database developers: Oracle® Database 12c introduced Identity, an
auto-incremental (system-generated) column to be used - commonly - for surrogate Primary Keys.
But not for Primary Keys only...
In the previous database versions (until 11g), you can implement an Identity by creating two
additional objects: a Sequence and a Trigger. You can avoid the Trigger, if you want, but you have
to manually invoke the NEXTVAL method of your Sequence object.
Now (from 12c onward) you can create your own Table and - as a part of its structure - define the
column that has to be generated as an Identity. No more and no less. And there is an intermediate
solution: in 12c you can manually create your own Sequence, and then use it as a default value for
your Table's column.
It’s interesting to note that Oracle introduced Identity column in this last release, almost at the
same time in which Microsoft® SQL Server® 2012 introduced Sequence objects.
This document doesn't want to be complete or exhaustive, but it shows you how to implement this
new feature. My name is neither Cary Millsap nor Jonathan Lewis so, please, forgive (and forget, as
soon as possible!) any possible mistake that I could have made over the pages which follow.
Please, feel free to contact me at my e-mail address (at the end of the document).
Should you need more details about the Identity column, you can find almost everything in Oracle ®
database documentation and in other sources over the internet (Tim Hall, for example, has written
really good articles on this topic, better than the one you are reading now).
I have written nothing new, obviously, but I hope you will find this document an interesting reading.
I will locally connect to an Oracle® 12c R1 database server, and I will use two different users: most
of the time I will use my user CONRAD (with all the privileges to create the needed objects, and with
unlimited quotas on my default Tablespace CONRAD_TAB, a Locally Managed Tablespace with
Automatic Segment Space Management) and, whenever a privileged user is required, I will use the
user SYS. I have not constrained my Tables with Primary Keys, because it's not necessary for my
test purposes.
If you need to create your test user from scratch, please refer to Appendix A at the end of this
document. Appendix B shows you how to clean your test environment.
Please, be careful: don't make your tests in a production environment, especially when connected
with SYSDBA privileges, as you could seriously damage your own data.
In the following examples, when needed, I have formatted the output for better readability.
Now I will show you two pre-12c solutions and two 12c ones.
Page 1
Oracle 12c Identity columns
(Corrado Piola)
Pre-12c solutions
For our test cases we need the following objects: a Table, a Sequence and a before-insert row-level
Trigger:
- Table T1 with a numeric column (we want to simulate the auto-incremental behavior)
- Sequence SEQ_T1_ID1 (to use for the number generation)
- Trigger TRG_T1_ID1 (that populates our column automatically)
Obviously you need the CREATE TABLE, CREATE SEQUENCE and - optionally - CREATE TRIGGER
privileges, and some quotas (maybe unlimited) on the default Tablespace (I will use the default
one).
First solution (Sequence + Trigger + Table)
First of all, I want show you the solution that includes the creation of a Trigger. This allows you to
avoid the explicit call to your Sequence in every INSERT statement.
Let's start:
[email protected]> CREATE TABLE T1
2 (ID1
NUMBER,
3
TNAME
VARCHAR2(128));
Table created.
[email protected]> CREATE SEQUENCE SEQ_T1_ID1;
Sequence created.
[email protected]> CREATE OR REPLACE TRIGGER TRG_T1_ID1
2 BEFORE INSERT ON T1
3 FOR EACH ROW
4 BEGIN
5
SELECT SEQ_T1_ID1.NEXTVAL INTO :NEW.ID1 FROM DUAL;
6 END;
7 /
Trigger created.
[email protected]>
And now you can insert your first record, without any reference to the Sequence:
[email protected]> INSERT INTO T1 (TNAME) VALUES ('VALUE 1');
1 row created.
[email protected]> SELECT * FROM T1 ORDER BY ID1;
ID1 TNAME
---------- -------------------------------------------------1 VALUE 1
[email protected]>
If you specify a value for the ID1 column, such a value is just ignored (thanks to the Trigger
definition):
[email protected]> INSERT INTO T1 (ID1, TNAME) VALUES (19, 'VALUE 2');
1 row created.
Page 2
Oracle 12c Identity columns
(Corrado Piola)
[email protected]> SELECT * FROM T1 ORDER BY ID1;
ID1
---------1
2
TNAME
-------------------------------------------------VALUE 1
VALUE 2
[email protected]>
Moreover, I can't insert a NULL, because the Trigger always generates the next value from the
Sequence (whether I supply a value or not):
[email protected]> INSERT INTO T1 (ID1, TNAME) VALUES (NULL, 'VALUE 3');
1 row created.
[email protected]> SELECT * FROM T1 ORDER BY ID1;
ID1
---------1
2
3
TNAME
-------------------------------------------------VALUE 1
VALUE 2
VALUE 3
[email protected]> COMMIT;
Commit complete.
[email protected]>
As you can see, the Trigger has automatically populated the column ID1 for us, for each case. That's
all.
But I can define my Trigger so that it accepts a supplied value too, if present:
[email protected]> CREATE OR REPLACE TRIGGER TRG_T1_ID1
2 BEFORE INSERT ON T1
3 FOR EACH ROW
4 WHEN (NEW.ID1 IS NULL)
5 BEGIN
6
SELECT SEQ_T1_ID1.NEXTVAL INTO :NEW.ID1 FROM DUAL;
7 END;
8 /
Trigger created.
[email protected]rcl> INSERT INTO T1 (ID1, TNAME) VALUES (19, 'VALUE 4');
1 row created.
[email protected]> SELECT * FROM T1 ORDER BY ID1;
ID1
---------1
2
3
19
TNAME
-------------------------------------------------VALUE 1
VALUE 2
VALUE 3
VALUE 4
[email protected]>
Still I can't insert a NULL, because the Trigger generates a value from the Sequence every time I
supply a NULL:
[email protected]> INSERT INTO T1 (ID1, TNAME) VALUES (NULL, 'VALUE 5');
1 row created.
Page 3
Oracle 12c Identity columns
(Corrado Piola)
[email protected]> SELECT * FROM T1 ORDER BY ID1;
ID1
---------1
2
3
4
19
TNAME
-------------------------------------------------VALUE 1
VALUE 2
VALUE 3
VALUE 5
VALUE 4
[email protected]> COMMIT;
Commit complete.
[email protected]>
Second solution (Sequence + Table)
The second solution, as I mentioned earlier, has no Trigger that generates the next value for you, so
you need to make an explicit call to Sequence's NEXTVAL method.
[email protected]> CREATE TABLE T2
2 (ID2
NUMBER,
3
TNAME
VARCHAR2(128));
Table created.
[email protected]> CREATE SEQUENCE SEQ_T2_ID2;
Sequence created.
[email protected]>
Now you have to manually specify the Sequence as part of the INSERT statement:
[email protected]> INSERT INTO T2 (ID2, TNAME) VALUES (SEQ_T2_ID2.NEXTVAL, 'VALUE 1');
1 row created.
[email protected]> SELECT * FROM T2 ORDER BY ID2;
ID2 TNAME
---------- -------------------------------------------------1 VALUE 1
[email protected]> COMMIT;
Commit complete.
[email protected]>
You can supply a NULL only if you have not defined a NOT NULL constraint on the ID2 column. Keep
in mind that the Table and the Sequence are completely independent from each other.
Page 4
Oracle 12c Identity columns
(Corrado Piola)
New 12c solutions
I will show you the two solutions that I mentioned in the Introduction: in the first one you need to
create a Sequence and your Table, in the second one you need your Table only.
First solution (Sequence + Table)
As a first approach I wish to show you the one I have previously called (see Introduction) as an
intermediate solution. You create a Sequence, independent from the Table, and then you set it as
the default value in the Table definition.
Let's start…
[email protected]> CREATE SEQUENCE SEQ_T3_ID3;
Sequence created.
[email protected]> CREATE TABLE T3
2 (ID3
NUMBER
DEFAULT SEQ_T3_ID3.NEXTVAL,
3
TNAME
VARCHAR2(128));
Table created.
[email protected]>
The Sequence is now part of the Table definition:
[email protected]> SELECT DBMS_METADATA.GET_DDL('TABLE', 'T3') FROM DUAL;
DBMS_METADATA.GET_DDL('TABLE','T3')
------------------------------------------------------------------CREATE TABLE "CONRAD"."T3"
(
"ID3" NUMBER DEFAULT "CONRAD"."SEQ_T3_ID3"."NEXTVAL",
"TNAME" VARCHAR2(128)
) SEGMENT CREATION DEFERRED
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
TABLESPACE "CONRAD_TAB"
[email protected]>
And in the [USER|ALL|DBA]_TAB_COLUMNS views we can see the DATA_DEFAULT column set:
[email protected]> SELECT COLUMN_ID, COLUMN_NAME, DATA_DEFAULT
2 FROM USER_TAB_COLUMNS
3 WHERE TABLE_NAME = 'T3'
4 ORDER BY COLUMN_ID;
COLUMN_ID
---------1
2
COLUMN_NAME
----------ID3
TNAME
DATA_DEFAULT
------------------------------"CONRAD"."SEQ_T3_ID3"."NEXTVAL"
[email protected]>
And we have no constraint:
[email protected]> SELECT *
2 FROM USER_CONSTRAINTS
3 WHERE TABLE_NAME = 'T3';
no rows selected
[email protected]>
Page 5
Oracle 12c Identity columns
(Corrado Piola)
We can finally insert our first value, without specifying the ID3 column:
[email protected]> INSERT INTO T3 (TNAME) VALUES ('VALUE 1');
1 row created.
[email protected]> SELECT * FROM T3;
ID3 TNAME
---------- -------------------------------------------------1 VALUE 1
[email protected]>
If we explicitly specify our ID3 value, Oracle® doesn't use the DEFAULT property defined for the ID3
column in the Table definition.
[email protected]> INSERT INTO T3 (ID3, TNAME) VALUES (19, 'VALUE 2');
1 row created.
[email protected]> SELECT * FROM T3 ORDER BY ID3;
ID3
---------1
19
TNAME
-------------------------------------------------VALUE 1
VALUE 2
[email protected]>
We can even insert a NULL, in the ID3 column (if I have not defined a NOT NULL constraint,
obviously):
[email protected]> INSERT INTO T3 (ID3, TNAME) VALUES (NULL, 'VALUE 3');
1 row created.
[email protected]> SELECT * FROM T3 ORDER BY ID3;
ID3
---------1
19
TNAME
-------------------------------------------------VALUE 1
VALUE 2
VALUE 3
[email protected]> COMMIT;
Commit complete.
[email protected]>
As we expected, the Sequence didn't miss any value:
[email protected]> SELECT SEQ_T3_ID3.NEXTVAL FROM DUAL;
NEXTVAL
---------2
[email protected]>
But there's another available option: I can tell Oracle® to use its DEFAULT value (the Sequence
NEXTVAL) even when I explicitly supply a NULL for the ID3 column: I use the DEFAULT ON NULL
property.
Page 6
Oracle 12c Identity columns
(Corrado Piola)
Please note that I have to delete any record which has ID3 set to NULL, in order to alter the table
structure as desired:
[email protected]> ALTER TABLE T3
2 MODIFY (ID3
NUMBER
DEFAULT ON NULL SEQ_T3_ID3.NEXTVAL);
ALTER TABLE T3
*
ERROR at line 1:
ORA-02296: cannot enable (CONRAD.) - null values found
[email protected]> DELETE FROM T3 WHERE ID3 IS NULL;
1 row deleted.
[email protected]> ALTER TABLE T3
2 MODIFY (ID3
NUMBER
DEFAULT ON NULL SEQ_T3_ID3.NEXTVAL);
Table altered.
[email protected]>
Now, when I insert a NULL I get the next available Sequence value:
[email protected]> INSERT INTO T3 (ID3, TNAME) VALUES (NULL, 'VALUE 3');
1 row created.
[email protected]> SELECT * FROM T3 ORDER BY ID3;
ID3
---------1
3
19
TNAME
-------------------------------------------------VALUE 1
VALUE 3
VALUE 2
[email protected]> COMMIT;
Commit complete.
[email protected]> SELECT COLUMN_ID, COLUMN_NAME, DATA_DEFAULT, DEFAULT_ON_NULL
2 FROM USER_TAB_COLUMNS
3 WHERE TABLE_NAME = 'T3'
4 ORDER BY COLUMN_ID;
COLUMN_ID
---------1
2
COLUMN_NAME
----------ID3
TNAME
DATA_DEFAULT
------------------------------"CONRAD"."SEQ_T3_ID3"."NEXTVAL"
DEF
--YES
NO
[email protected]>
And thanks to DBMS_METADATA.GET_DDL function we can see the new definition of the ID3 column:
"ID3" NUMBER DEFAULT "CONRAD"."SEQ_T3_ID3"."NEXTVAL" NOT NULL ENABLE
As a matter of fact, now we can see a new constraint:
[email protected]> SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, SEARCH_CONDITION
2 FROM USER_CONSTRAINTS
3 WHERE TABLE_NAME = 'T3';
CONSTRAINT_NAME CONSTRAINT_TYPE SEARCH_CONDITION
--------------- --------------- ----------------SYS_C0011098
C
"ID3" IS NOT NULL
[email protected]>
Page 7
Oracle 12c Identity columns
(Corrado Piola)
Another interesting thing to note is that I can drop the Sequence, even if it's used as a DEFAULT
value in a Table definition:
[email protected]> DROP SEQUENCE SEQ_T3_ID3;
Sequence dropped.
[email protected]>
Obviously this DDL command has committed our Transaction, so we don't need to issue a COMMIT.
Despite the fact that I have dropped the Sequence, I can still see it in the Table definition:
[email protected]> SELECT DBMS_METADATA.GET_DDL('TABLE', 'T3') FROM DUAL;
DBMS_METADATA.GET_DDL('TABLE','T3')
-----------------------------------------------------------------CREATE TABLE "CONRAD"."T3"
(
"ID3" NUMBER DEFAULT "CONRAD"."SEQ_T3_ID3"."NEXTVAL",
"TNAME" VARCHAR2(128)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 4194304 NEXT 4194304 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "CONRAD_TAB"
[email protected]>
But what happens when we try to insert a record into the Table?
It succeeds in the only case we explicitly specify a value for the ID3 column, otherwise it tries to use
the Sequence and it fails:
[email protected]> INSERT INTO T3 (ID3, TNAME) VALUES (37, 'VALUE 4');
1 row created.
[email protected]> INSERT INTO T3 (TNAME) VALUES ('VALUE 5');
INSERT INTO T3 (TNAME) VALUES ('VALUE 5')
*
ERROR at line 1:
ORA-02289: sequence does not exist
[email protected]>
So, finally, commit our transaction and let's see the final Table's content:
[email protected]> COMMIT;
Commit complete.
[email protected]> SELECT * FROM T3;
ID3
---------1
3
19
37
TNAME
-------------------------------------------------VALUE 1
VALUE 3
VALUE 2
VALUE 4
[email protected]>
Page 8
Oracle 12c Identity columns
(Corrado Piola)
Second solution (Table)
This approach is really simple, because you don't need to create either a Trigger or a Sequence, and
your Table is all that you need: a Table with a numeric column that has the Identity, autoincremental, property. Oracle®, behind the scenes, creates the Sequence for you.
Please note: the owner of the Table (CONRAD, in my case) must have both the CREATE TABLE
privilege, as well as the CREATE SEQUENCE. And when you create your Table you don't immediately
need quotas on the Tablespace you create the Table in, not until you insert your first record (thanks
to Deferred Segment Creation, from 11gR2 onward).
The Identity column must be numeric. You almost always define it as NUMBER (with neither precision
nor scale), but you can specify a precision too, if you want. In this latter case pay attention that if
you reach the limit that you specify, Oracle® throws an error (as you will see in one of the following
examples) because the system-generated Sequence is independent of your column precision.
Let's try this…
[email protected]> CREATE TABLE T4
2 (ID4
NUMBER
GENERATED AS IDENTITY,
3
TNAME
VARCHAR2(128));
Table created.
[email protected]> INSERT INTO T4 (TNAME) VALUES ('VALUE 1');
1 row created.
[email protected]> SELECT * FROM T4;
ID4 TNAME
---------- -------------------------------------------------1 VALUE 1
[email protected]> COMMIT;
Commit complete.
[email protected]>
We obtained the same behavior and the same result, but with much less work.
In the [USER|ALL|DBA]_TABLES views we can easily see whether a Table contains an Identity
column (T4) or not (T1, T2, T3):
[email protected]> SELECT TABLE_NAME, HAS_IDENTITY
2 FROM USER_TABLES
3 WHERE TABLE_NAME IN ('T1', 'T2', 'T3', 'T4')
4 ORDER BY TABLE_NAME;
TABLE_NAME
---------T1
T2
T3
T4
HAS
--NO
NO
NO
YES
[email protected]>
And in the [USER|ALL|DBA]_TAB_COLUMNS views we can see which column has the Identity property
(ID4):
[email protected]> SELECT TABLE_NAME, COLUMN_NAME, IDENTITY_COLUMN
2 FROM USER_TAB_COLUMNS
3 WHERE TABLE_NAME IN ('T1', 'T2', 'T3', 'T4')
4 ORDER BY TABLE_NAME, COLUMN_ID;
Page 9
Oracle 12c Identity columns
TABLE_NAME
---------T1
T1
T2
T2
T3
T3
T4
T4
COLUMN_NAME
----------ID1
TNAME
ID2
TNAME
ID3
TNAME
ID4
TNAME
(Corrado Piola)
IDE
--NO
NO
NO
NO
NO
NO
YES
NO
8 rows selected.
[email protected]>
Another way to look at the Identity columns is by the [USER|ALL|DBA]_TAB_IDENTITY_COLS views:
[email protected]> SELECT TABLE_NAME, COLUMN_NAME, GENERATION_TYPE, IDENTITY_OPTIONS
2 FROM USER_TAB_IDENTITY_COLS
3 ORDER BY TABLE_NAME;
TABLE_NAME
---------T4
COLUMN_NAME
----------ID4
GENERATION
---------ALWAYS
IDENTITY_OPTIONS
-----------------START WITH: 1, INCREMENT BY: 1,
MAX_VALUE: 9999999999999999999999999999,
MIN_VALUE: 1, CYCLE_FLAG: N,
CACHE_SIZE: 20, ORDER_FLAG: N
[email protected]>
The GENERATION_TYPE column shows you that Oracle® will ALWAYS populate the ID4 column with a
Sequence value for you. That's the default behavior, as you can see, because I have not specified
the ALWAYS keyword earlier in my CREATE TABLE command. I could have created my Table as:
CREATE TABLE T4
(ID4
NUMBER
GENERATED ALWAYS AS IDENTITY,
TNAME
VARCHAR2(128));
That's the same.
With the ALWAYS option, if I explicitly specify a value for the ID4 column in my INSERT statement, I
get an error:
[email protected]> INSERT INTO T4 (ID4, TNAME) VALUES (19, 'VALUE 2');
INSERT INTO T4 (ID4, TNAME) VALUES (19, 'VALUE 2')
*
ERROR at line 1:
ORA-32795: cannot insert into a generated always identity column
[email protected]>
Oracle® has automatically created a NOT NULL Check Constraint (I have not explicitly specified "NOT
NULL" in my CREATE TABLE statement), and this constraint is "Not Deferrable" and "Immediate",
too:
[email protected]> SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME,
2
SEARCH_CONDITION, DEFERRABLE, DEFERRED
3 FROM USER_CONSTRAINTS
4 WHERE TABLE_NAME IN ('T1', 'T2', 'T3', 'T4')
5 ORDER BY TABLE_NAME, CONSTRAINT_NAME;
CONSTRAINT_NAME
--------------SYS_C0011088
C
C
TABLE_NAME
---------T4
SEARCH_CONDITION
----------------"ID4" IS NOT NULL
DEFERRABLE
-------------NOT DEFERRABLE
DEFERRED
--------IMMEDIATE
[email protected]>
Page 10
Oracle 12c Identity columns
(Corrado Piola)
And it has automatically created a Sequence for us, too. So, let's see all these Sequences
(manually created and system-generated):
[email protected]> SELECT * FROM USER_SEQUENCES
2 ORDER BY SEQUENCE_NAME;
SEQUENCE_NAME
------------ISEQ$$_95174
SEQ_T1_ID1
SEQ_T2_ID2
ORDER_FLAG
---------N
N
N
MIN_VALUE
--------1
1
1
CACHE_SIZE
---------20
20
20
MAX_VALUE
---------------------------9999999999999999999999999999
9999999999999999999999999999
9999999999999999999999999999
LAST_NUMBER
----------21
21
21
PARTITION_COUNT
---------------
INCREMENT_BY
-----------1
1
1
SESSION_FLAG
-----------N
N
N
CYCLE_FLAG
---------N
N
N
KEEP_VALUE
---------N
N
N
[email protected]>
Remember: we have three Sequences only - not four - because we have dropped one of them…
Furthermore, we can see the name of the automatically created Sequence in the DATA_DEFAULT
column of the [USER|ALL|DBA]_TAB_COLUMNS views:
[email protected]> SELECT TABLE_NAME, COLUMN_NAME, DATA_DEFAULT
2 FROM USER_TAB_COLUMNS
3 WHERE IDENTITY_COLUMN = 'YES'
4 ORDER BY TABLE_NAME, COLUMN_ID;
TABLE_NAME
---------T4
COLUMN_NAME
----------ID4
DATA_DEFAULT
------------------------------"CONRAD"."ISEQ$$_95174".nextval
[email protected]>
Finally, for the sake of completeness, we obviously see that there's only one Trigger, as we
expected: the one we have manually created in the first pre-12c solution, because the Identity
doesn't need it:
[email protected]> SELECT TRIGGER_NAME, TRIGGER_TYPE FROM USER_TRIGGERS;
TRIGGER_NAME
-----------TRG_T1_ID1
TRIGGER_TYPE
--------------BEFORE EACH ROW
[email protected]>
From the USER_SEQUENCES output shown earlier, we can see that all the Sequence options are
exactly the same for both (manual and automatic). So Oracle® has automatically created the
Sequence with all the default options.
To be more precise, in SYS.SEQ$ we can see a different FLAGS bitmap (the sixth bit is set to "1" for
the system-generated Sequence):
- In the "manually" created Sequence, we have a bitmap that's something like "0000001000"
- In the "automatically" created one, we have a different bitmap: something like "0000101000"
I might be wrong, however I have never seen the sixth bit set to "1" for Sequences other than
system-generated ones.
You can find references to the fourth bit (decode(bitand(flags, 8), 8, 1, 0)) in the following Views:
- LOGSTDBY_SUPPORT (CURRENT_SBY column) in 10gR2
- LOGSTDBY_SUPPORT_SEQ (CURRENT_SBY column) in 11gR2 and 12cR1
In 12c we can see a reference to the seventh bit (SESSION_FLAG) and the tenth bit (KEEP_VALUE)
in the [USER|ALL|DBA]_SEQUENCES views.
Page 11
Oracle 12c Identity columns
(Corrado Piola)
[email protected]> SELECT O.NAME, S.FLAGS
2 FROM SYS.SEQ$ S, OBJ$ O
3 WHERE S.OBJ# = O.OBJ#
4
AND O.NAME IN ('ISEQ$$_95174', 'SEQ_T1_ID1', 'SEQ_T2_ID2')
5 ORDER BY O.NAME;
NAME
--------------ISEQ$$_95174
SEQ_T1_ID1
SEQ_T2_ID2
FLAGS
------40
8
8
[email protected]>
The Sequence created by Oracle® behind the scenes has a system-generated name that refers to
the Table's Object Id (95174 in my case):
[email protected]> SELECT OBJECT_NAME FROM USER_OBJECTS WHERE OBJECT_ID = 95174;
OBJECT_NAME
-----------------------T4
[email protected]>
The Sequence name contains the Object Id of the Table to which is bounded, so we can't have more
than one Identity column per Table (this is a more than acceptable limit, because it's uncommon to
have a situation in which you need more than one Identity column in the same Table).
We can't drop a system-generated Sequence:
[email protected]> DROP SEQUENCE ISEQ$$_95174;
DROP SEQUENCE ISEQ$$_95174
*
ERROR at line 1:
ORA-32794: cannot drop a system-generated Sequence
[email protected]>
And we can't modify it:
[email protected]> ALTER SEQUENCE ISEQ$$_95174 INCREMENT BY 2;
ALTER SEQUENCE ISEQ$$_95174 INCREMENT BY 2
*
ERROR at line 1:
ORA-32793: cannot alter a system-generated Sequence
[email protected]>
Moreover, I can use this system-generated Sequence as if it were a regular Sequence,
independently of the Table (for our purpose it does not make sense, of course).
Let's generate, for example, the next value:
[email protected]> SELECT ISEQ$$_95174.NEXTVAL FROM DUAL;
NEXTVAL
---------21
[email protected]>
Now this value (21) has been lost, obviously, as we can see if we insert another record into the
Table.
So, as with every Sequence object, we are never guaranteed about a sequential numbering.
Page 12
Oracle 12c Identity columns
(Corrado Piola)
[email protected]> INSERT INTO T4 (TNAME) VALUES ('VALUE 2');
1 row created.
[email protected]> SELECT * FROM T4;
ID4
---------1
22
TNAME
-------------------------------------------------VALUE 1
VALUE 2
[email protected]> COMMIT;
Commit complete.
[email protected]>
By default, Oracle® caches 20 values for a Sequence - we can see it from the CACHE_SIZE column so the LAST_NUMBER value is the next value we'll get in case we lose the cached ones (for example
after an Instance restart, or after a Shared Pool flushing):
[email protected]> SELECT SEQUENCE_NAME, CACHE_SIZE, LAST_NUMBER
2 FROM USER_SEQUENCES
3 ORDER BY SEQUENCE_NAME;
SEQUENCE_NAME
------------ISEQ$$_95174
SEQ_T1_ID1
SEQ_T2_ID2
CACHE_SIZE
---------20
20
20
LAST_NUMBER
----------41
21
21
[email protected]>
[email protected]> ALTER SYSTEM FLUSH SHARED_POOL;
System altered.
[email protected]>
The NEXTVAL will be 41, instead of 23:
[email protected]> SELECT ISEQ$$_95174.NEXTVAL FROM DUAL;
NEXTVAL
---------41
[email protected]> SELECT SEQUENCE_NAME, CACHE_SIZE, LAST_NUMBER
2 FROM USER_SEQUENCES
3 ORDER BY SEQUENCE_NAME;
SEQUENCE_NAME
------------ISEQ$$_95174
SEQ_T1_ID1
SEQ_T2_ID2
CACHE_SIZE
---------20
20
20
LAST_NUMBER
----------61
21
21
[email protected]>
So, the LAST_NUMBER column is something like a High-water Mark of the Sequence (although it's a
"next" value, and not a "last" value). In fact in the SYS.SEQ$ view, on which the
[USER|ALL|DBA]_SEQUENCES views are based, is named HIGHWATER:
Page 13
Oracle 12c Identity columns
(Corrado Piola)
[email protected]> SELECT O.OBJ#, O.NAME, S.CACHE, S.HIGHWATER
2 FROM SYS.SEQ$ S, SYS.OBJ$ O
3 WHERE S.OBJ# = O.OBJ# AND O.NAME = 'ISEQ$$_95174';
OBJ#
----95175
NAME
-----------ISEQ$$_95174
CACHE
----20
HIGHWATER
--------61
[email protected]>
It's interesting to see how Oracle® created the Table with the Identity column:
[email protected]> SELECT DBMS_METADATA.GET_DDL('TABLE', 'T4') FROM DUAL;
DBMS_METADATA.GET_DDL('TABLE','T4')
-------------------------------------------------------------------------------CREATE TABLE "CONRAD"."T4"
(
"ID4" NUMBER GENERATED ALWAYS AS IDENTITY
MINVALUE 1 MAXVALUE 9999999999999999999999999999
INCREMENT BY 1 START WITH 1 CACHE 20
NOORDER NOCYCLE NOT NULL ENABLE,
"TNAME" VARCHAR2(128)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 4194304 NEXT 4194304 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "CONRAD_TAB"
[email protected]>
It contains all the parameters of a full Sequence definition.
This is useful, because we can define this Identity in a handful of ways. For example we could define
our Identity so that it starts with a different value, or with a different incremental gap (INCREMENT
BY).
For example, let’s suppose we want to start with the value 500, and we want an increment by 10.
With the default CACHE=20 we have cached in the Shared Pool all the values between 500 and 690.
[email protected]> CREATE TABLE T
2 (ID
NUMBER
GENERATED ALWAYS AS IDENTITY
3
INCREMENT BY 10 START WITH 500,
4
TNAME
VARCHAR2(128));
Table created.
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE 1');
1 row created.
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE 2');
1 row created.
[email protected]> SELECT * FROM T;
ID
---------500
510
TNAME
-------------------------------------------------VALUE 1
VALUE 2
[email protected]> COMMIT;
Commit complete.
[email protected]>
Page 14
Oracle 12c Identity columns
(Corrado Piola)
Modifying the Identity parameters
We can modify the Table structure and its related Sequence parameters. In this case Oracle®
modifies the Sequence definition but it obviously maintains the same name, because the Sequence
is bound to the Table's Object Id.
Now we'll see that if we change the START WITH parameter, Oracle® recreates (resets) the Sequence
(we can't change the START WITH parameter of a traditional Sequence, as a matter of fact).
But if we change some other parameters, Oracle® doesn't recreate the Sequence and keeps its
current values.
After every change, we will see the output from the DBMS_METADATA.GET_DDL function and the
USER_SEQUENCES view.
We can use the Table T from the previous example. So, first of all, we have to find the systemgenerated Sequence name:
[email protected]> SELECT COLUMN_NAME, DATA_DEFAULT
2 FROM USER_TAB_COLUMNS
3 WHERE TABLE_NAME = 'T' AND IDENTITY_COLUMN = 'YES';
COLUMN_NAME
----------ID
DATA_DEFAULT
-------- ---------------------"CONRAD"."ISEQ$$_95196".nextval
[email protected]>
The last generated value, in the previous example, was 510. So, the next available value in the
cache is 520. In case we lose all the cached values, we will get 700:
[email protected]> SELECT ISEQ$$_95196.NEXTVAL FROM DUAL;
NEXTVAL
---------520
[email protected]>
Let's see the current Sequence settings (remember, from the previous example, that we have
specified our custom, non-default, increment and starting values):
MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 10
START WITH 500 CACHE 20 NOORDER NOCYCLE NOT NULL ENABLE
SEQUENCE_NAME
------------ISEQ$$_95196
ORDER_FLAG
---------N
MIN_VALUE
--------1
CACHE_SIZE
---------20
MAX_VALUE
---------------------------9999999999999999999999999999
LAST_NUMBER
----------700
PARTITION_COUNT
---------------
INCREMENT_BY
-----------10
SESSION_FLAG
-----------N
CYCLE_FLAG
---------N
KEEP_VALUE
---------N
Now, for example, let's modify the increment (INCREMENT BY) and caching (CACHE) parameters:
[email protected]> ALTER TABLE T
2 MODIFY (ID NUMBER
3
4
GENERATED ALWAYS AS IDENTITY
INCREMENT BY 50
NOCACHE);
Table altered.
[email protected]>
Page 15
Oracle 12c Identity columns
(Corrado Piola)
MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 50
START WITH 500 NOCACHE NOORDER NOCYCLE NOT NULL ENABLE
SEQUENCE_NAME
------------ISEQ$$_95196
ORDER_FLAG
---------N
MIN_VALUE
--------1
CACHE_SIZE
---------0
MAX_VALUE
---------------------------9999999999999999999999999999
LAST_NUMBER
----------570
PARTITION_COUNT
---------------
INCREMENT_BY
-----------50
SESSION_FLAG
-----------N
CYCLE_FLAG
---------N
KEEP_VALUE
---------N
The last generated value was 520. But now we don't have cached values, so the next value to be
generated - with the new increment of 50 - is 570, as we can see:
[email protected]> SELECT ISEQ$$_95196.NEXTVAL FROM DUAL;
NEXTVAL
---------570
[email protected]>
So Oracle® has not reset the Sequence, but it has used the new INCREMENT value to calculate the
next to be generated.
MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 50
START WITH 500 NOCACHE NOORDER NOCYCLE NOT NULL ENABLE
SEQUENCE_NAME
------------ISEQ$$_95196
ORDER_FLAG
---------N
MIN_VALUE
--------1
CACHE_SIZE
---------0
MAX_VALUE
---------------------------9999999999999999999999999999
LAST_NUMBER
----------620
PARTITION_COUNT
---------------
INCREMENT_BY
-----------50
SESSION_FLAG
-----------N
CYCLE_FLAG
---------N
KEEP_VALUE
---------N
Now, let's modify the starting value (START WITH) only:
[email protected]> ALTER TABLE T
2 MODIFY (ID NUMBER
3
GENERATED ALWAYS AS IDENTITY
START WITH 100);
Table altered.
[email protected]>
MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1
START WITH 100 CACHE 20 NOORDER NOCYCLE NOT NULL ENABLE
SEQUENCE_NAME
------------ISEQ$$_95196
ORDER_FLAG
---------N
MIN_VALUE
--------1
CACHE_SIZE
---------20
MAX_VALUE
---------------------------9999999999999999999999999999
LAST_NUMBER
----------100
PARTITION_COUNT
---------------
INCREMENT_BY
-----------1
SESSION_FLAG
-----------N
CYCLE_FLAG
---------N
KEEP_VALUE
---------N
As you can see, Oracle® has reset the current parameters (INCREMENT BY and CACHE) to the default
ones. And then, with a new starting value of 100, the next value to be generated has to be 100.
[email protected]> SELECT ISEQ$$_95196.NEXTVAL FROM DUAL;
NEXTVAL
---------100
[email protected]>
Page 16
Oracle 12c Identity columns
(Corrado Piola)
After the first number generation (100), thanks to the caching of the next 20 values, the next to be
generated (in the case of loss of all the cached ones) is 120.
MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1
START WITH 100 CACHE 20 NOORDER NOCYCLE NOT NULL ENABLE
SEQUENCE_NAME
------------ISEQ$$_95196
ORDER_FLAG
---------N
MIN_VALUE
--------1
CACHE_SIZE
---------20
MAX_VALUE
---------------------------9999999999999999999999999999
LAST_NUMBER
----------120
PARTITION_COUNT
---------------
INCREMENT_BY
-----------1
SESSION_FLAG
-----------N
CYCLE_FLAG
---------N
KEEP_VALUE
---------N
You have seen that by changing the START WITH parameter, Oracle® recreates the Sequence from
scratch. In fact, you can't modify the starting value in a manually created Sequence, at least not
with the ALTER SEQUENCE START WITH command:
[email protected]> CREATE SEQUENCE SEQ_T;
Sequence created.
[email protected]> ALTER SEQUENCE SEQ_T START WITH 4;
ALTER SEQUENCE SEQ_T START WITH 4
*
ERROR at line 1:
ORA-02283: cannot alter starting sequence number
[email protected]> DROP SEQUENCE SEQ_T;
Sequence dropped.
[email protected]>
Note: you can change the next value to be generated with a little work-around, without recreating
the Sequence, playing with the INCREMENT BY setting (the START WITH setting remains unchanged,
anyway). Keep in mind that there's no CREATE OR REPLACE SEQUENCE command, so should you
need to recreate the Sequence you have to drop it, recreate it and re-grant the needed privileges to
the users.
"By Default" clause
By default (the ALWAYS clause), as previously explained, we can't insert a value into a column
created as Identity:
[email protected]> INSERT INTO T (ID, TNAME) VALUES (19, 'VALUE 3');
INSERT INTO T (ID, TNAME) VALUES (19, 'VALUE 3')
*
ERROR at line 1:
ORA-32795: cannot insert into a generated always identity column
[email protected]>
If we need to insert such "free" values, independently of the Identity mechanism, we have to specify
the BY DEFAULT clause, instead of ALWAYS.
[email protected]> ALTER TABLE T
2 MODIFY (ID NUMBER
GENERATED BY DEFAULT AS IDENTITY);
Table altered.
[email protected]>
Page 17
Oracle 12c Identity columns
(Corrado Piola)
This command has not altered any Sequence parameter. They are the same as before the DDL
command.
MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1
START WITH 100 CACHE 20 NOORDER NOCYCLE NOT NULL ENABLE
SEQUENCE_NAME
------------ISEQ$$_95196
ORDER_FLAG
---------N
MIN_VALUE
--------1
CACHE_SIZE
---------20
MAX_VALUE
---------------------------9999999999999999999999999999
LAST_NUMBER
----------120
PARTITION_COUNT
---------------
INCREMENT_BY
-----------1
SESSION_FLAG
-----------N
CYCLE_FLAG
---------N
KEEP_VALUE
---------N
And you can recognize Identity columns with BY DEFAULT property set:
[email protected]> SELECT TABLE_NAME, COLUMN_NAME, GENERATION_TYPE
2 FROM USER_TAB_IDENTITY_COLS
3 ORDER BY TABLE_NAME;
TABLE_NAME
---------T
T4
COLUMN_NAME
----------ID
ID4
GENERATION
---------BY DEFAULT
ALWAYS
[email protected]>
So, now, I can specify my Identity column or not:
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE 3');
1 row created.
[email protected]> INSERT INTO T (ID, TNAME) VALUES (19, 'VALUE 4');
1 row created.
[email protected]> SELECT * FROM T ORDER BY ID;
ID
---------19
120
500
510
TNAME
----------------VALUE 4
VALUE 3
VALUE 1
VALUE 2
[email protected]> COMMIT;
Commit complete.
[email protected]>
But I can't supply a NULL for the Identity column:
[email protected]rcl> INSERT INTO T (ID, TNAME) VALUES (NULL, 'VALUE 5');
INSERT INTO T (ID, TNAME) VALUES (NULL, 'VALUE 5')
*
ERROR at line 1:
ORA-01400: cannot insert NULL into ("CONRAD"."T"."ID")
[email protected]>
The real problem with the possibility of inserting both system generated values and user-supplied
ones, is that in this way we may have conflicts, because we may insert the same value more than
once: with the auto-increment Sequence and manually. Obviously, this doesn't happen if you define
the Identity column as a Primary Key, or you put a Unique constraint on it (in both cases you will
have a unique constraint violation).
But you may take advantage of this capability, for example, to get back a lost value.
Page 18
Oracle 12c Identity columns
(Corrado Piola)
"By Default on NULL" clause
If we want to have the possibility of specifying a NULL for the Identity column (in the previous
example we got an error), we need the BY DEFAULT ON NULL clause.
But - pay attention - this doesn't mean that you can insert a NULL. Absolutely not! Remember that
Identity columns are - by definition - NOT NULL constrained.
So, if you specify a NULL, instead of getting the ORA-01400 error, your insert will succeed; but
Oracle® will generate the Sequence NEXTVAL for you, and the NULL will be ignored.
So, for example, these two commands are equivalent:
INSERT INTO T (ID, TNAME) VALUES (NULL, 'VALUE X');
INSERT INTO T (TNAME) VALUES ('VALUE X');
Let's try this:
[email protected]> ALTER TABLE T
2 MODIFY (ID NUMBER
GENERATED BY DEFAULT ON NULL AS IDENTITY);
Table altered.
[email protected]>
We can recognize Identity columns with BY DEFAULT ON NULL property:
[email protected]> SELECT COLUMN_ID, COLUMN_NAME, DATA_DEFAULT,
2
IDENTITY_COLUMN, DEFAULT_ON_NULL
3 FROM USER_TAB_COLUMNS
4 WHERE TABLE_NAME = 'T'
5 ORDER BY COLUMN_ID;
COLUMN_ID
---------1
2
COLUMN_NAME
----------ID
TNAME
DATA_DEFAULT
-----------"CONRAD"."ISEQ$$_95196".nextval
IDE
--YES
NO
DEF
--YES
NO
[email protected]>
If we try to insert a NULL, as I previously stated, we get:
[email protected]> INSERT INTO T (ID, TNAME) VALUES (NULL, 'VALUE 5');
1 row created.
[email protected]> SELECT * FROM T ORDER BY ID;
ID
---------19
120
121
500
510
TNAME
-------------------------VALUE 4
VALUE 3
VALUE 5
VALUE 1
VALUE 2
[email protected]> COMMIT;
Commit complete.
[email protected]>
Page 19
Oracle 12c Identity columns
(Corrado Piola)
Dropping a Table with an Identity column
When we drop a Table with an Identity column, the system-generated Sequence will be dropped too,
but in the only case you specify the PURGE option. Otherwise it will remain until you purge the
Recycle Bin.
Let's try with our Table T and its Sequence ISEQ$$_95196:
[email protected]> SELECT SEQUENCE_NAME
2 FROM USER_SEQUENCES
3 WHERE SEQUENCE_NAME = 'ISEQ$$_95196';
SEQUENCE_NAME
-------------------ISEQ$$_95196
[email protected]> DROP TABLE T;
Table dropped.
[email protected]> SELECT SEQUENCE_NAME
2 FROM USER_SEQUENCES
3 WHERE SEQUENCE_NAME = 'ISEQ$$_95196';
SEQUENCE_NAME
-------------------ISEQ$$_95196
[email protected]> PURGE RECYCLEBIN;
Recyclebin purged.
[email protected]> SELECT SEQUENCE_NAME
2 FROM USER_SEQUENCES
3 WHERE SEQUENCE_NAME = 'ISEQ$$_95196';
no rows selected
[email protected]>
Additional restrictions
The Datatype for an Identity column must be Numeric:
[email protected]> CREATE TABLE T
2 (ID
VARCHAR2(11)
GENERATED ALWAYS AS IDENTITY);
(ID
VARCHAR2(11)
GENERATED ALWAYS AS IDENTITY)
*
ERROR at line 2:
ORA-30675: identity column must be a numeric type
[email protected]>
As I previously mentioned, we can't define more than one Identity column per Table:
[email protected]> CREATE TABLE T
2 (ID1
NUMBER
GENERATED ALWAYS AS IDENTITY,
3
ID2
NUMBER
GENERATED ALWAYS AS IDENTITY);
ID2
NUMBER
GENERATED ALWAYS AS IDENTITY)
*
ERROR at line 3:
ORA-30669: table can have only one identity column
[email protected]>
Page 20
Oracle 12c Identity columns
(Corrado Piola)
As any other Sequence, all the values generated are not rollbackable. If you open a Transaction,
generate some values, and then issue a ROLLBACK command, those values are lost:
[email protected]> CREATE TABLE T
2 (ID
NUMBER
GENERATED ALWAYS AS IDENTITY,
3
TNAME
VARCHAR2(128));
Table created.
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE 1');
1 row created.
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE 2');
1 row created.
[email protected]> SELECT * FROM T ORDER BY ID;
ID
---------1
2
TNAME
--------------------------VALUE 1
VALUE 2
[email protected]> ROLLBACK;
Rollback complete.
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE 1');
1 row created.
[email protected]> SELECT * FROM T ORDER BY ID;
ID
TNAME
---------3
--------------------------VALUE 1
[email protected]> COMMIT;
Commit complete.
[email protected]>
If we create a Table with a CTAS (Create Table … As Select …) command from a Table containing an
Identity column, the new Table will not inherit the Identity column properties, but only the NUMBER
datatype and the NOT NULL constraint:
[email protected]> CREATE TABLE T_NEW
2 AS
3 SELECT ID, TNAME
4 FROM T;
Table created.
[email protected]> SELECT DBMS_METADATA.GET_DDL('TABLE', 'T') FROM DUAL;
DBMS_METADATA.GET_DDL('TABLE','T')
-------------------------------------------------------------------------------CREATE TABLE "CONRAD"."T"
(
"ID" NUMBER GENERATED ALWAYS AS IDENTITY
MINVALUE 1 MAXVALUE 9999999999999999999999999999
INCREMENT BY 1 START WITH 1 CACHE 20
NOORDER NOCYCLE NOT NULL ENABLE,
"TNAME" VARCHAR2(128)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 4194304 NEXT 4194304 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
Page 21
Oracle 12c Identity columns
(Corrado Piola)
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "CONRAD_TAB"
[email protected]> SELECT DBMS_METADATA.GET_DDL('TABLE', 'T_NEW') FROM DUAL;
DBMS_METADATA.GET_DDL('TABLE','T_NEW')
-------------------------------------------------------------------------------CREATE TABLE "CONRAD"."T_NEW"
(
"ID" NUMBER NOT NULL ENABLE,
"TNAME" VARCHAR2(128)
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 4194304 NEXT 4194304 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "CONRAD_TAB"
[email protected]> DROP TABLE T PURGE;
Table dropped.
[email protected]> DROP TABLE T_NEW PURGE;
Table dropped.
[email protected]>
We can’t modify a Table’s column from NUMBER to Identity (even if the Table is empty):
[email protected]> CREATE TABLE T
2 (ID
NUMBER,
3
TNAME
VARCHAR2(128));
Table created.
[email protected]> ALTER TABLE T
2 MODIFY (ID
NUMBER
GENERATED AS IDENTITY);
MODIFY (ID
NUMBER
GENERATED AS IDENTITY)
*
ERROR at line 2:
ORA-30673: column to be modified is not an identity column
[email protected]> DROP TABLE T PURGE;
Table dropped.
[email protected]>
Another thing to note is that a TRUNCATE TABLE command doesn’t reset the Identity column starting
value. If you have a little experience with Microsoft® SQL Server®, for example, you know that after
such a command you’ll get the Identity column reset too.
[email protected]> CREATE TABLE T
2 (ID
NUMBER(1)
GENERATED AS IDENTITY,
3
TNAME
VARCHAR2(128));
Table created.
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE 1');
1 row created.
[email protected]> SELECT * FROM T ORDER BY ID;
ID
---------1
TNAME
-------------------VALUE 1
Page 22
Oracle 12c Identity columns
(Corrado Piola)
[email protected]> TRUNCATE TABLE T;
Table truncated.
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE 1');
1 row created.
[email protected]> SELECT * FROM T ORDER BY ID;
ID
---------2
TNAME
-------------------VALUE 1
[email protected]> DROP TABLE T PURGE;
Table dropped.
[email protected]>
What about errors?
Does any error cause a loss of the next Sequence value? Obviously not… not, of course, if Oracle®
detects it before the statement execution. Let’s see a couple of examples:
[email protected]> CREATE TABLE T
2 (ID
NUMBER
GENERATED ALWAYS AS IDENTITY,
3
TNAME
VARCHAR2(128));
Table created.
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE 1');
1 row created.
[email protected]> INSERT INTO T (ID, TNAME) VALUES (19, 'VALUE 2');
INSERT INTO T (ID, TNAME) VALUES (19, 'VALUE 2')
*
ERROR at line 1:
ORA-32795: cannot insert into a generated always identity column
[email protected]> INSERT INTO T (ID, TNAME) VALUES (19, 'VALUE 2');
INSERT INTO T (ID, TNAME) VALUES (19, 'VALUE 2')
*
ERROR at line 1:
ORA-32795: cannot insert into a generated always identity column
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE 2');
1 row created.
[email protected]> SELECT * FROM T ORDER BY ID;
ID
---------1
2
TNAME
-------------------VALUE 1
VALUE 2
[email protected]>
We get two consecutive errors but we preserve the next Sequence value (2). Oracle® throws the
exception before the statement execution (the Sequence value hasn’t been generated yet).
Page 23
Oracle 12c Identity columns
(Corrado Piola)
But what happens if I get an error about the datatype precision?
For example, let’s create an Identity column with precision “1” (the maximum allowed value will be
“9”) and define the Sequence with a starting value of “9” so that the second INSERT statement will
fail.
[email protected]> DROP TABLE T PURGE;
Table dropped.
[email protected]> CREATE TABLE T
2 (ID
NUMBER(1)
GENERATED AS IDENTITY
3
START WITH 9,
4
TNAME
VARCHAR2(128));
Table created.
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE X');
1 row created.
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE X');
INSERT INTO T (TNAME) VALUES ('VALUE X')
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
[email protected]> INSERT INTO T (TNAME) VALUES ('VALUE X');
INSERT INTO T (TNAME) VALUES ('VALUE X')
*
ERROR at line 1:
ORA-01438: value larger than specified precision allowed for this column
[email protected]> SELECT * FROM T ORDER BY ID;
ID
---------9
TNAME
-------------------VALUE X
[email protected]> SELECT COLUMN_NAME, DATA_DEFAULT
2 FROM USER_TAB_COLUMNS
3 WHERE TABLE_NAME = 'T' AND IDENTITY_COLUMN = 'YES';
COLUMN_NAME
----------ID
DATA_DEFAULT
-------------------------------------------------------"CONRAD"."ISEQ$$_95329".nextval
[email protected]> SELECT "CONRAD"."ISEQ$$_95329".NEXTVAL FROM DUAL;
NEXTVAL
---------12
[email protected]>
This time we have lost two values (10 and 11), because the Sequence values have been generated
before getting the error about the precision.
[email protected]> DROP TABLE T PURGE;
Table dropped.
[email protected]>
Page 24
Oracle 12c Identity columns
(Corrado Piola)
A basic performance test for INSERT statements
It's interesting to create a little performance test for the discussed solutions. I'll use the
OBJECT_NAME column from the DBA_OBJECTS view to populate the TNAME fields, and I will measure
the total elapsed time. Furthermore, I will use my four Tables T1, T2, T3 and T4, because they are
still alive...
I will execute each of the following blocks of SQL commands for three consecutive times, and I
will report the best elapsed time for each of the four solutions.
Both the two 12c solutions use the Sequence as a default value for the IDx column, so they almost
have the same execution time.
First of all, we need to prepare our environment:
For the first test I will use the following version of the Trigger (I have presented two different
versions, earlier):
[email protected]> CREATE OR REPLACE TRIGGER TRG_T1_ID1
2 BEFORE INSERT ON T1
3 FOR EACH ROW
4 BEGIN
5
SELECT SEQ_T1_ID1.NEXTVAL INTO :NEW.ID1 FROM DUAL;
6 END;
7 /
Trigger created.
[email protected]>
For the third test we need to recreate our Sequence SEQ_T3_ID3 that we have dropped in our
earlier tests.
[email protected]> CREATE SEQUENCE SEQ_T3_ID3;
Sequence created.
[email protected]>
And now we ask SQL*Plus to show us the query elapsed times:
[email protected]> set timing on
Let's start!
1. First Pre-12c solution (Sequence + Trigger + Table)
My best execution time:
[email protected]> TRUNCATE TABLE T1 REUSE STORAGE;
Table truncated.
Elapsed: 00:00:00.51
[email protected]> INSERT INTO T1 (TNAME)
2 SELECT OBJECT_NAME
3 FROM DBA_OBJECTS;
93013 rows created.
Elapsed: 00:00:08.64
[email protected]>
Page 25
Oracle 12c Identity columns
(Corrado Piola)
2. Second Pre-12c solution (Sequence + Table)
My best execution time:
[email protected]> TRUNCATE TABLE T2 REUSE STORAGE;
Table truncated.
Elapsed: 00:00:00.31
[email protected]> INSERT INTO T2 (ID2, TNAME)
2 SELECT SEQ_T2_ID2.NEXTVAL, OBJECT_NAME
3 FROM DBA_OBJECTS;
93013 rows created.
Elapsed: 00:00:01.62
[email protected]>
3. First 12c solution (Sequence + Table)
My best execution time:
[email protected]> TRUNCATE TABLE T3 REUSE STORAGE;
Table truncated.
Elapsed: 00:00:00.34
[email protected]> INSERT INTO T3 (TNAME)
2 SELECT OBJECT_NAME
3 FROM DBA_OBJECTS;
93013 rows created.
Elapsed: 00:00:01.63
[email protected]>
4. Second 12c solution (Table)
My best execution time:
[email protected]> TRUNCATE TABLE T4 REUSE STORAGE;
Table truncated.
Elapsed: 00:00:00.12
[email protected]> INSERT INTO T4 (TNAME)
2 SELECT OBJECT_NAME
3 FROM DBA_OBJECTS;
93013 rows created.
Elapsed: 00:00:01.45
[email protected]>
We can see that there's a huge difference between the worst method (1. Pre-12c Trigger-based) and
the best one (4. 12c with the Table only): from 8.64 seconds to 1.45 seconds.
The second 12c solution has an Elapsed time which is about the 16.8% of the pre-12c one
(~1/6).
If we consider the pre-12c second solution (explicit use of the Sequence NEXTVAL), yet, we have a
little difference in the elapsed times. So, if you are not using the 12c release, avoid the Triggerbased approach - if possible - and make use of a Sequence only.
Page 26
Oracle 12c Identity columns
(Corrado Piola)
Appendix A
First of all, I create – connected as SYS - my test Tablespace and my test User:
C:\>sqlplus sys as sysdba
SQL*Plus: Release 12.1.0.1.0 Production on Tue Sep 3 17:40:35 2013
Copyright (c) 1982, 2013, Oracle.
All rights reserved.
Enter password:
Connected to:
Oracle Database 12c Enterprise Edition Release 12.1.0.1.0 - 64bit Production
With the Partitioning, OLAP, Advanced Analytics and Real Application Testing opt
ions
[email protected]> CREATE TABLESPACE CONRAD_TAB
2 DATAFILE 'C:\app\oracle\oradata\orcl\CONRAD_1.DBF'
3
SIZE 20M AUTOEXTEND ON NEXT 10M
4 EXTENT MANAGEMENT LOCAL
5 SEGMENT SPACE MANAGEMENT AUTO;
Tablespace created.
[email protected]> CREATE USER CONRAD
2 IDENTIFIED BY conrad
3 DEFAULT TABLESPACE CONRAD_TAB
4 QUOTA UNLIMITED ON CONRAD_TAB;
User created.
[email protected]>
Then, I need my privileges:
[email protected]> GRANT CREATE SESSION TO CONRAD;
Grant succeeded.
[email protected]> GRANT CREATE TABLE TO CONRAD;
Grant succeeded.
[email protected]> GRANT CREATE SEQUENCE TO CONRAD;
Grant succeeded.
[email protected]> GRANT CREATE TRIGGER TO CONRAD;
Grant succeeded.
[email protected]> GRANT SELECT ON DBA_OBJECTS TO CONRAD;
Grant succeeded.
[email protected]>
And finally I can connect as my test User:
[email protected]> conn conrad
Enter password:
Connected.
[email protected]>
Page 27
Oracle 12c Identity columns
(Corrado Piola)
Appendix B
Clean your test environment.
[email protected]> set timing off
If you want to drop the whole test environment:
[email protected]> DROP USER CONRAD CASCADE;
User dropped.
[email protected]> DROP TABLESPACE CONRAD_TAB INCLUDING CONTENTS AND DATAFILES;
Tablespace dropped.
[email protected]>
Otherwise, if you want to drop the Objects only, but not the User and the Tablespace:
[email protected]> DROP TABLE T1 PURGE;
Table dropped.
[email protected]> DROP TABLE T2 PURGE;
Table dropped.
[email protected]> DROP TABLE T3 PURGE;
Table dropped.
[email protected]> DROP TABLE T4 PURGE;
Table dropped.
[email protected]> DROP SEQUENCE SEQ_T1_ID1;
Sequence dropped.
[email protected]> DROP SEQUENCE SEQ_T2_ID2;
Sequence dropped.
[email protected]> DROP SEQUENCE SEQ_T3_ID3;
Sequence dropped.
[email protected]>
And, finally, you can disconnect your session.
Oracle copyright and trademark
Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their
respective owners. Copyright © 1982, 2013, Oracle and/or its affiliates. All rights reserved.
Corrado Piola
Owner and Database solutions Director at Systrategy Srl
[email protected]
www.systrategy.it
Oracle Database 10g, 11g Administrator Certified Professional
Oracle Database 11g Performance Tuning Certified Expert
Oracle Database SQL Certified Expert, Oracle Advanced PL-SQL Developer Certified Professional
Page 28
Download
Related flashcards
Computer science

25 Cards

ARM architecture

23 Cards

X86 architecture

22 Cards

Computer science

29 Cards

System software

24 Cards

Create flashcards