Oracle實現自增列(序列+觸發器實現、Identity Columns新特性、序列作為預設值)

lhrbest發表於2018-11-15

Oracle實現自增列(序列+觸發器實現、Identity Columns新特性、序列作為預設值)


https://www.cnblogs.com/xqzt/p/4452292.html


Sequence+Trigger實現Oracle列自增

序列的語法格式為:

CREATE SEQUENCE 序列名[INCREMENT BY n][START WITH n][{MAXVALUE/MINVALUE n|NOMAXVALUE}][{CYCLE|NOCYCLE}][{CACHE n|NOCACHE}];

INCREMENT BY

用於定義序列的步長,如果省略,則預設為1,如果出現負值,則代表序列的值是按照此步長遞減的。

START WITH

定義Oracle序列的初始值(即產生的第一個值),預設為1。

MAXVALUE

定義序列生成器能產生的最大值。選項NOMAXVALUE是預設選項,代表沒有最大值定義,這時對於遞增序列,系統能夠產生的最大值是10的27次方;對於遞減序列,最大值是-1。

MINVALUE

定義序列生成器能產生的最小值。選項NOMAXVALUE是預設選項,代表沒有最小值定義,

CYCLE 和NOCYCLE

表示當序列生成器的值達到限制值後是否迴圈。CYCLE代表迴圈,NOCYCLE代表不迴圈。如果迴圈,則當遞增序列達到最大值時,迴圈到最小值;對於遞減序列達到最小值時,迴圈到最大值。如果不迴圈,達到限制值後,繼續產生新值就會發生錯誤。


CACHE

(緩衝)定義存放序列的記憶體塊的大小,預設為20。NOCACHE表示不對序列進行記憶體緩衝。對序列進行記憶體緩衝,可以改善序列的效能。

例子:

建立測試表

---建立測試表CREATE TABLE departments (
    ID NUMBER (10) NOT NULL,
    DESCRIPTION VARCHAR2 (50) NOT NULL);--新增主鍵ALTER TABLE departments ADD (CONSTRAINT dept_pk PRIMARY KEY (ID));


建立序列

--建立序列CREATE SEQUENCE dept_seq;

等同於

CREATE SEQUENCE dept_seq MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE NOPARTITION

觸發器版本1: 非空時,才使用序列的NEXTVAL值


CREATE OR REPLACE TRIGGER dept_before_insert
BEFORE INSERT ON departments
FOR EACH ROWWHEN (NEW.ID IS NULL)BEGINSELECT dept_seq.NEXTVAL INTO :NEW.ID FROM dual ;END ;


觸發器版本2 : 無視插入資料時,指定的ID


CREATE OR REPLACE TRIGGER dept_before_insert
BEFORE INSERT ON departments 
FOR EACH ROWBEGINSELECT dept_seq.NEXTVAL INTO :new.id FROM dual;END;


測試資料

INSERT INTO DEPARTMENTS (ID, DESCRIPTION) VALUES (TEST_USER.DEPT_SEQ.NEXTVAL, '指定ID');INSERT INTO DEPARTMENTS (DESCRIPTION) VALUES ('不指定ID');



Oracle列自增-Identity Columns in Oracle Database 12c Release 1 (12.1)

https://www.cnblogs.com/xqzt/p/4455149.html

在ORACLE 12C以前的版本中,如果要實現列自增長,需要透過序列+觸發器實現,到了12C ORACLE 引進了Identity Columns新特性,從而實現了列自增長功能。

一、Identity Columns使用語法

GENERATED[ ALWAYS | BY DEFAULT [ ON NULL ] ]AS IDENTITY [ ( identity_options ) ]identity_options

二、

clip_image001

2.1 ALWAYS選項


DROP TABLE IDENTITY_TEST_TAB PURGE;CREATE TABLE identity_test_tab (
id NUMBER GENERATED ALWAYS AS IDENTITY,
description VARCHAR2(30)
);


插入測試1:

INSERT INTO identity_test_tab (description) VALUES ('Just DESCRIPTION')

[SQL]INSERT INTO identity_test_tab (description) VALUES ('Just DESCRIPTION')

受影響的行: 1

時間: 0.008s

插入測試2:

INSERT INTO identity_test_tab (id, description) VALUES (NULL, 'ID=NULL and DESCRIPTION')

[SQL]INSERT INTO identity_test_tab (id, description) VALUES (NULL, 'ID=NULL and DESCRIPTION')

[Err] ORA-32795: cannot insert into a generated always identity column 無法插入到“始終生成”身份列

插入測試3:

INSERT INTO identity_test_tab (id, description) VALUES (999, 'ID=999 and DESCRIPTION')

[SQL]INSERT INTO identity_test_tab (id, description) VALUES (999, 'ID=999 and DESCRIPTION')

[Err] ORA-32795: cannot insert into a generated always identity column

clip_image002

更新測試:

UPDATE IDENTITY_TEST_TAB SET ID=2 WHERE ID=1

[SQL]UPDATE IDENTITY_TEST_TAB SET ID=2 WHERE ID=1

[Err] ORA-32796: cannot update a generated always identity column

結論:

  1. GENERATED ALWAYS AS IDENTITY 可以不指定該列進行插入

  2. GENERATED ALWAYS AS IDENTITY不能在該列中插入NULL值

  3. GENERATED ALWAYS AS IDENTITY不能指定具體值插入

  4. GENERATED ALWAYS AS IDENTITY 不能使用update更新該列

2.2 BY DEFAULT 選項


DROP TABLE identity_test_tab PURGE;CREATE TABLE identity_test_tab (
id NUMBER GENERATED BY DEFAULT AS IDENTITY,
description VARCHAR2(30)
);


插入測試1:

INSERT INTO identity_test_tab (description) VALUES ('Just DESCRIPTION');

[SQL]INSERT INTO identity_test_tab (id, description) VALUES (999, 'ID=999 and DESCRIPTION')

受影響的行: 1

時間: 0.001s

SELECT * FROM identity_test_tab;

clip_image0021

插入測試2:

INSERT INTO identity_test_tab (id, description) VALUES (999, 'ID=999 and DESCRIPTION');

[SQL]INSERT INTO identity_test_tab (id, description) VALUES (999, 'ID=999 and DESCRIPTION')

受影響的行: 1

時間: 0.001s

SELECT * FROM identity_test_tab;

clip_image003

插入測試3:

INSERT INTO identity_test_tab (id, description) VALUES (NULL, 'ID=NULL and DESCRIPTION');

[SQL]INSERT INTO identity_test_tab (id, description) VALUES (NULL, 'ID=NULL and DESCRIPTION')

[Err] ORA-01400: cannot insert NULL into ("TEST_USER"."IDENTITY_TEST_TAB"."ID")

clip_image003

更新測試:

UPDATE IDENTITY_TEST_TAB SET ID=2 WHERE ID=1

[SQL]UPDATE IDENTITY_TEST_TAB SET ID=2 WHERE ID=1

受影響的行: 1

時間: 0.001sUPDATE IDENTITY_TEST_TAB SET ID=2 WHERE ID=1

     結論:

  1. GENERATED BY DEFAULT AS IDENTITY 可以不指定該列進行插入

  2. GENERATED BY DEFAULT AS IDENTITY 可以指定具體值插入

  3. GENERATED BY DEFAULT AS IDENTITY 不能在該列中插入null值

  4. 可以使用update更新該列,但不能更新為NULL

2.3 DEFAULT ON NULL 選項


DROP TABLE identity_test_tab PURGE;CREATE TABLE identity_test_tab (
id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
description VARCHAR2(30)
);


插入測試:

INSERT INTO identity_test_tab (description) VALUES ('Just DESCRIPTION');INSERT INTO identity_test_tab (id, description) VALUES (999, 'ID=999 and DESCRIPTION');INSERT INTO identity_test_tab (id, description) VALUES (NULL, 'ID=NULL and DESCRIPTION');

[SQL]INSERT INTO identity_test_tab (description) VALUES ('Just DESCRIPTION')

受影響的行: 1

時間: 0.003s

[SQL]INSERT INTO identity_test_tab (id, description) VALUES (999, 'ID=999 and DESCRIPTION')

受影響的行: 1

時間: 0.001s

[SQL]INSERT INTO identity_test_tab (id, description) VALUES (NULL, 'ID=NULL and DESCRIPTION')

受影響的行: 1

時間: 0.002s

SELECT * FROM identity_test_tab;

clip_image004

更新測試

UPDATE IDENTITY_TEST_TAB SET ID=3 WHERE ID=1

[SQL]UPDATE IDENTITY_TEST_TAB SET ID=3 WHERE ID=1

受影響的行: 1

時間: 0.004s

     結論:

  1. GENERATED BY DEFAULT ON NULL AS IDENTITY 可以不指定該列進行插入

  2. GENERATED BY DEFAULT ON NULL AS IDENTITY 方式可以指定具體值插入

  3. GENERATED BY DEFAULT ON NULL AS IDENTITY 可以在該列中插入null值

  4. 可以使用update更新該列

三、原理

3.1 Identity Columns 是基於序列實現的

使用此語法實現ID自增,要求必須有建立序列的許可權。可以推測是基於序列實現的

執行完建表語句後:


CREATE TABLE identity_test_tab (
id NUMBER GENERATED ALWAYS AS IDENTITY,
description VARCHAR2(30)
);


檢視user_objects

SELECT object_name, object_type FROM user_objects;

clip_image005

發現建表的時候自動生成了一個sequence


SELECT table_name, 
column_name,
generation_type,
identity_optionsFROM all_tab_identity_colsWHERE owner = 'TEST_USER';


clip_image006

表和sequence的關係存在 SYS.IDNSEQ$ 表中

Sys 登陸檢視


SELECT a.name AS table_name,
b.name AS sequence_nameFROM sys.idnseq$ cJOIN obj$ a ON c.obj# = a.obj#JOIN obj$ b ON c.seqobj# = b.obj#where a.name='IDENTITY_TEST_TAB';


clip_image007

3.2 GENERATED IDENTITY 中sequence不能單獨被刪除

DROP TABLE IDENTITY_TEST_TAB

刪除表後,該sequence還存在。且該sequence無法被刪除

clip_image008

必須

purge table IDENTITY_TEST_TAB,

結論:

  1. Identity Columns 是基於序列實現的

  2. GENERATED IDENTITY 中sequence不能單獨被刪除

  3. GENERATED IDENTITY 中的表刪除,如果存在回收站中,該sequence依然儲存,如果表被徹底刪除,則sequence也被刪除

3.3 執行插入語句時的解釋計劃

INSERT INTO identity_test_tab (description) VALUES ('Just DESCRIPTION');

clip_image009

 對比發現:該方式的效率比觸發器+序列的方式高!

四、

clip_image010

檢視錶的DDL

select dbms_metadata.get_ddl('TABLE','IDENTITY_TEST_TAB') FROM DUAL;

CREATE TABLE "TEST_USER"."IDENTITY_TEST_TAB"

( "ID" NUMBER GENERATED ALWAYS AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE NOT NULL ENABLE,

"DESCRIPTION" VARCHAR2(30)

) SEGMENT CREATION IMMEDIATE

PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255

NOCOMPRESS LOGGING

STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645

PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1

BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)

TABLESPACE "USERS"

INCREMENT BY

用於定義序列的步長,如果省略,則預設為1,如果出現負值,則代表序列的值是按照此步長遞減的。

START WITH

定義Oracle序列的初始值(即產生的第一個值),預設為1。

MAXVALUE

定義序列生成器能產生的最大值。選項NOMAXVALUE是預設選項,代表沒有最大值定義,這時對於遞增序列,系統能夠產生的最大值是10的27次方;對於遞減序列,最大值是-1。

MINVALUE

定義序列生成器能產生的最小值。選項NOMAXVALUE是預設選項,代表沒有最小值定義,

CYCLE 和NOCYCLE

表示當序列生成器的值達到限制值後是否迴圈。CYCLE代表迴圈,NOCYCLE代表不迴圈。如果迴圈,則當遞增序列達到最大值時,迴圈到最小值;對於遞減序列達到最小值時,迴圈到最大值。如果不迴圈,達到限制值後,繼續產生新值就會發生錯誤。

CACHE

(緩衝)定義存放序列的記憶體塊的大小,預設為20。NOCACHE表示不對序列進行記憶體緩衝。對序列進行記憶體緩衝,可以改善序列的效能。

原文地址:


Oracle列自增實現(3)-DEFAULT Values Using Sequences

http://www.cnblogs.com/xqzt/p/4455171.html

Oracle 12c中,可以使用序列的 NEXTVAL  and  CURRVAL 的值作為預設值,來實現列自增!

一、使用序列的 NEXTVAL  and  CURRVAL 的值作為預設值

建立序列

CREATE SEQUENCE t1_seq;

建表

CREATE TABLE t1 ( 
id NUMBER DEFAULT t1_seq.NEXTVAL, 
description VARCHAR2(30) 
);

插入資料

INSERT INTO t1 (description) VALUES ('DESCRIPTION only');INSERT INTO t1 (id, description) VALUES (999, 'ID=999 and DESCRIPTION');INSERT INTO t1 (id, description) VALUES (NULL, 'ID=NULL and DESCRIPTION');

查詢結果

SELECT * FROM t1;

clip_image001

二、預設值明確為非空

建立兩個序列

CREATE SEQUENCE default_seq;CREATE SEQUENCE default_on_null_seq;

建表,col1和col2分別使用上面兩個序列的NEXTVAL作為預設值,其中col2  DEFAULT   ON   NULL

CREATE TABLE t2 (
col1 NUMBER DEFAULT default_seq.NEXTVAL,
col2 NUMBER DEFAULT ON NULL default_on_null_seq.NEXTVAL,
description VARCHAR2(30)
);

插入資料

INSERT INTO t2 (description) VALUES ('DESCRIPTION only');INSERT INTO t2 (col1, col2, description) VALUES (999, 999, '999,999,DESCRIPTION');INSERT INTO t2 (col1, col2, description) VALUES (NULL, NULL, 'NULL,NULL,DESCRIPTION');

查詢資料,可以看到col2位NULL時候,被預設轉換使用了default_on_null_seq.NEXTVAL的

SELECT * FROM t2;

clip_image002

三、例子:主從表的簡單例子

CREATE SEQUENCE master_seq;

CREATE SEQUENCE detail_seq;

CREATE TABLE master (

id NUMBER DEFAULT master_seq.NEXTVAL,

description VARCHAR2(30)

);

CREATE TABLE detail (

id NUMBER DEFAULT detail_seq.NEXTVAL,

master_id NUMBER DEFAULT master_seq.CURRVAL,

description VARCHAR2(30)

);

INSERT INTO master (description) VALUES ('Master 1');

INSERT INTO detail (description) VALUES ('Detail 1');

INSERT INTO detail (description) VALUES ('Detail 2');

INSERT INTO master (description) VALUES ('Master 2');

INSERT INTO detail (description) VALUES ('Detail 3');

INSERT INTO detail (description) VALUES ('Detail 4');

SELECT * FROM master;

clip_image003

SELECT * FROM detail;

clip_image004

原文:



Oracle 12C新特性-使用序列做列的預設值


Oracle 12C開始,支援使用序列來做列的預設值,今天測試一下,本測試環境是Oracle 12.2.0.1.0 for linux x86_64版本。

view source

1 SQL> select * from v$version;
2
3 BANNER                                                                               CON_ID
4 -------------------------------------------------------------------------------- ----------
5 Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production              0
6 PL/SQL Release 12.2.0.1.0 - Production                                                    0
7 CORE    12.2.0.1.0      Production                                                        0
8 TNS for Linux: Version 12.2.0.1.0 - Production                                            0
9 NLSRTL Version 12.2.0.1.0 - Production                                                    0

透過字面能就能看出,想用序列做列的預設值,肯定是要先有序列,下面建立一個名字為SEQ_1的序列。

1 SQL> create sequence seq_1 minvalue 1 maxvalue 99999 start with 1 increment by 1 cache 20 nocycle;      
2
3 Sequence created.

可以在建表的時候指定使用序列作為列的預設值,也可以透過ALTER TABLE的方式來設定。

01 SQL> create table t_test01(id number default seq_1.nextval,name varchar2(50));
02
03 Table created.
04 SQL> drop table t_test01 purge;
05
06 Table dropped.
07
08 SQL> create table t_test01(id number,name varchar2(50));
09
10 Table created.
11
12 SQL> alter table t_test01 modify id number default seq_1.nextval;
13
14 Table altered.

和其他常規的預設值一樣,可以透過USER_TAB_COLUMNS檢視來檢視列的預設值資訊。

1 SQL> select TABLE_NAME,COLUMN_NAME,DATA_DEFAULT from user_tab_columns where TABLE_NAME='T_TEST01' and COLUMN_NAME='ID';
2
3 TABLE_NAME COLUMN_NAME     DATA_DEFAULT
4 ---------- --------------- ------------------------------
5 T_TEST01   ID              "DBDREAM"."SEQ_1"."NEXTVAL"

像這張表插入資料時,如果不指定ID欄位,就會使用序列的值作為預設值。

1 SQL> insert into t_test01 (name) values ('xxx');
2
3 1 row created.
4 SQL> select * from t_test01;
5
6          ID NAME
7 ---------- -------
8           1 xxx

在向這張表插入資料時,如果指定ID欄位並使用’’或者null值,都不會所以用序列作為預設值。

01 SQL> insert into t_test01 values ('','yyy');
02
03 1 row created.
04 SQL> commit;
05
06 Commit complete.
07 SQL> select * from t_test01;
08
09          ID NAME
10 ---------- --------
11           1 xxx
12             yyy
13 SQL> insert into t_test01 values (null,'zzz');
14
15 1 row created.
16 SQL> commit;
17
18 Commit complete.
19 SQL> select * from t_test01;
20
21          ID NAME
22 ---------- --------
23           1 xxx
24             yyy
25             zzz

在向這張表插入資料時,指定default關鍵字,就會使用序列的值作為預設值。

01 SQL> insert into t_test01 values (default,'aaa');
02
03 1 row created.
04 SQL> commit;
05
06 Commit complete.
07 SQL> select * from t_test01;
08
09          ID NAME
10 ---------- ------
11           1 xxx
12             yyy
13             zzz
14           2 aaa

下面測試下有唯一約束的情況下,序列值和資料重複的情況,先為表新增一個唯一約束。

1 SQL> alter table t_test01 add constraint un_1 unique(id);
2
3 Table altered.

序列的下一個值是3,將一條記錄的值修改為3。

01 SQL> update t_test01 set id=3 where name='yyy';
02
03 1 row updated.
04 SQL> commit;
05
06 Commit complete.
07 SQL> select * from t_test01;
08
09          ID NAME
10 ---------- ------
11           1 xxx
12           3 yyy
13             zzz
14           2 aaa

然後使用序列值插入資料。

1 SQL> insert into t_test01 values (default,'bbb');
2 insert into t_test01 values (default,'bbb')
3 *
4 ERROR at line 1:
5 ORA-00001: unique constraint (DBDREAM.UN_1) violated

這裡遇到了違反唯一性約束的錯誤。這時,序列的當前值變成了4,update操作的時候也可以使用序列的值。

01 SQL> update t_test01 set id=default where name='zzz';
02
03 1 row updated.
04 SQL> commit;
05
06 Commit complete.
07 SQL> select * from t_test01;
08
09          ID NAME
10 ---------- --------
11           1 xxx
12           3 yyy
13           4 zzz
14           2 aaa

刪除序列,表的預設值資訊不會跟著改變,這時向表中插入資料,如果使用到序列作為預設值,就會報錯。

01 SQL> drop sequence seq_1;
02
03 Sequence dropped.
04 SQL> select TABLE_NAME,COLUMN_NAME,DATA_DEFAULT from user_tab_columns where TABLE_NAME='T_TEST01' and COLUMN_NAME='ID';
05
06 TABLE_NAME COLUMN_NAME     DATA_DEFAULT
07 ---------- --------------- ------------------------------
08 T_TEST01   ID              "DBDREAM"."SEQ_1"."NEXTVAL"
09 SQL> insert into t_test01 (name) values('ddd');
10 insert into t_test01 (name) values('ddd')
11            *
12 ERROR at line 1:
13 ORA-02289: sequence does not exist

把這個序列重新建立出來,如果有唯一約束,需要注意序列的起始值,這個問題就可以解決。

下面再測試下其他使用者操作這張用序列做為列的預設值的表的情況。

先重新初始化一下測試環境。

1 SQL> create sequence seq_1 minvalue 1 maxvalue 99999 start with 1 increment by 1 cache 20 nocycle;      
2
3 Sequence created.
4 SQL> truncate table t_test01;
5
6 Table truncated.

然後建立測試使用者,並授予插入這張表的許可權。

1 SQL> create user u_test identified by dbdream;
2
3 User created.
4 SQL> grant connect,resource to u_test;
5
6 Grant succeeded.
7 SQL> grant insert on dbdream.t_test01 to u_test;
8
9 Grant succeeded.

然後切換到這個新建的使用者,向測試表插入一條資料。

1 SQL> conn u_test/dbdream@localhost/pdb1
2 Connected.
3 SQL> insert into dbdream.t_test01 (name) values('abc');
4 insert into dbdream.t_test01 (name) values('abc')
5            *
6 ERROR at line 1:
7 ORA-00942: table or view does not exist

因為沒有授予這個使用者訪問測試表使用的序列的許可權,所以這裡遇到了錯誤,這個錯誤竟然是表或檢視不存在,看到這個錯誤很容易誤以為是沒有這張表的訪問許可權,其實是沒有訪問序列的許可權。

01 SQL> conn / as sysdba
02 Connected.
03 SQL> alter session set container=pdb1;
04
05 Session altered.
06 SQL> grant select on dbdream.seq_1 to u_test;
07
08 Grant succeeded.
09 SQL> conn u_test/dbdream@localhost/pdb1
10 Connected.
11 SQL> insert into dbdream.t_test01 (name) values('abc');
12
13 1 row created.

授予這個使用者查詢測試序列的許可權後,成功插入資料。

也就是說,其他使用者操作這樣的表時要使用序列做預設值,不但要有操作表的許可權,還要有訪問相關序列的許可權才可以。





來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26736162/viewspace-2220097/,如需轉載,請註明出處,否則將追究法律責任。

相關文章