Identity Columns

标识列是12c的一个新特性,能自动生成列里的值,一部分功能与mysql中的自增列有点类似,简化了应用程序的开发和其他关系型数据库迁移到oracle。这个特性主要是利用一个默认的序列生成器,通过这个生成器来绝对字段增加或者减少的整数值。

drop table t1 purge;
create table t1
(id number generated by default as identity,
name varchar2(10));

insert into t1 (name) values ('xb');
commit;

select * from t1;

        ID NAME
---------- ----------
         1 xb

对于标识列来说,我们要么指定一个默认的序列去生成字段值或者只对那些插入NULL的情况生成字段值。根据标识列的定义可以有GENERATED ALWAYSGENERATED BY DEFAULT关键字。

GENERATED ALWAYS

oracle使用序列生成器来确定列值,但也可以显示的指定某个值。如果指定了ON NULL关键字那么序列生成器会在你插入NULL值的时候生成一个值给字段。

drop table t1 purge;
create table t1
(id number generated by default as identity,
name varchar2(10));

insert into t1 (name) values ('xb');
commit;

xb@PDB12C> insert into t1 values(null,'xb2');
insert into t1 values(null,'xb2')
                      *
ERROR at line 1:
ORA-01400: cannot insert NULL into ("XB"."T1"."ID")

select * from t1;

        ID NAME
---------- ----------
         1 xb

xb@PDB12C> @desc t1
           Name                            Null?    Type
           ------------------------------- -------- ----------------------------
    1      ID                              NOT NULL NUMBER
    2      NAME                                     VARCHAR2(10)

可以看到通过序列生成器生成的字段在创建的时候默认就是NOT NULL属性,因为这里没有对NULL的情况进行指定,所以序列生成器在碰到NULL的情况无法生成值,则会报错

加上ON NULL关键字

drop table t1 purge;
create table t1
(id number generated by default on NULL as identity,
name varchar2(10));

insert into t1 values(1,'xb');
insert into t1 values(9,'xb2');
insert into t1 values(null,'xb3');
commit;

xb@PDB12C> select * from t1;

        ID NAME
---------- ----------
         1 xb
         9 xb2
         1 xb3

这样当插入的字段值为NULL时,生成器则会自动生成一个整数值给字段。通过执行计划也可以看到,在对有序列生成器属性的表插入数据时,其实是访问的序列

xb@PDB12C> insert into t1 values(null,'xb3');

1 row created.

xb@PDB12C> @x
Display execution plan for last statement for this session from library cache...

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID  4b1kmg40ra83t, child number 0
-------------------------------------
insert into t1 values(null,'xb3')

Plan hash value: 948190082

---------------------------------------------------------
| Id  | Operation                | Name         | Cost  |
---------------------------------------------------------
|   0 | INSERT STATEMENT         |              |     1 |
|   1 |  LOAD TABLE CONVENTIONAL | T1           |       |
|   2 |   SEQUENCE               | ISEQ$$_90071 |       |
---------------------------------------------------------

   - cpu costing is off (consider enabling it)


16 rows selected.

# 而这个序列也是真实存在的,说明是用户自动创建的
xb@PDB12C> select * from user_sequences;

SEQUENCE_NAME         MIN_VALUE  MAX_VALUE INCREMENT_BY C O CACHE_SIZE LAST_NUMBER S E S K
-------------------- ---------- ---------- ------------ - - ---------- ----------- - - - -
ISEQ$$_90071                  1 1.0000E+28            1 N N         20          21 N N N N

Generated Always

字段无法被用户所更新,所以序列生成器生成的值是列唯一可能的值,如果像之前一样指定某个值或修改都会报错

drop table t2 purge;
create table t2
(id number generated always as identity,
name varchar2(10));

xb@PDB12C> insert into t2 values(1,'xb');
insert into t2 values(1,'xb')
*
ERROR at line 1:
ORA-32795: cannot insert into a generated always identity column

xb@PDB12C> insert into t2 values(null,'xb');
insert into t2 values(null,'xb')
*
ERROR at line 1:
ORA-32795: cannot insert into a generated always identity column

insert into t2(name) values('xb');
insert into t2(name) values('xb2');
insert into t2(name) values('xb3');
commit;

xb@PDB12C> select * from t2;

        ID NAME
---------- ----------
         1 xb
         2 xb2
         3 xb3

xb@PDB12C> update t2 set id=4 where name='xb';
update t2 set id=4 where name='xb'
              *
ERROR at line 1:
ORA-32796: cannot update a generated always identity column

由于是使用的序列,那么也可以用到一些序列中的特性,可以指定不同的初始值和累加值

drop table t3 purge;
create table t3
(id number generated always as identity (start with 2 increment by 2),
name varchar2(10));

insert into t3(name) values('xb');
insert into t3(name) values('xb2');
insert into t3(name) values('xb3');
commit;

xb@PDB12C> select * from t3;

        ID NAME
---------- ----------
         2 xb
         4 xb2
         6 xb3

通过USER_TAB_IDENTITY_COLS等视图可以查询到一些标识列的信息

set linesize 300
col table_name for a10
col COLUMN_NAME for a10
col IDENTITY_OPTIONS for a70
select * from USER_TAB_IDENTITY_COLS;

TABLE_NAME COLUMN_NAM GENERATION SEQUENCE_NAME        IDENTITY_OPTIONS
---------- ---------- ---------- -------------------- ----------------------------------------------------------------------
T3         ID         ALWAYS     ISEQ$$_90075         START WITH: 2, INCREMENT BY: 2, MAX_VALUE: 999999999999999999999999999
                                                      9, MIN_VALUE: 1, CYCLE_FLAG: N, CACHE_SIZE: 20, ORDER_FLAG: N

T1         ID         BY DEFAULT ISEQ$$_90079         START WITH: 1, INCREMENT BY: 1, MAX_VALUE: 999999999999999999999999999
                                                      9, MIN_VALUE: 1, CYCLE_FLAG: N, CACHE_SIZE: 20, ORDER_FLAG: N

T2         ID         ALWAYS     ISEQ$$_90073         START WITH: 1, INCREMENT BY: 1, MAX_VALUE: 999999999999999999999999999
                                                      9, MIN_VALUE: 1, CYCLE_FLAG: N, CACHE_SIZE: 20, ORDER_FLAG: N

这个特性目前也有一些限制条件:

  • 每张表只能由一个标识列
  • 标识列的数据类型必须是数字型,而且不能是用户自定义的类型
  • 标识列中不能有DEFAULT关键字
  • 标识列中,NOT NULLNON DEFERRABLE约束都是隐式存在
  • CTAS语句无法继承字段的标识属性