| 特性 | Sequence + Trigger(11g 及以前) | Identity Column(12c+) |
|---|---|---|
| 語法複雜度 | 建立 Sequence、Trigger 兩個物件 | 單一 GENERATED AS IDENTITY 子句 |
| 效能 | 觸發器每列開銷 | 內核層級實作,零開銷 |
| 匯入匯出 | 序列值易錯位 | 原生支援正確遷移 |
| 指定值插入 | Trigger 邏輯控制 | GENERATED BY DEFAULT 選項 |
| 相容性 | 所有版本 | 12.1+ |
CREATE TABLE s_depart (
depart_id NUMBER NOT NULL,
depart_name NVARCHAR2(40) NOT NULL,
depart_order NUMBER DEFAULT 0,
CONSTRAINT pk_s_depart PRIMARY KEY (depart_id)
);
CREATE SEQUENCE s_s_depart
MINVALUE 1
MAXVALUE 999999999999999999999999999
START WITH 1
INCREMENT BY 1
NOCACHE
NOORDER; -- RAC 環境建議 ORDER 保證全域有序
參數解析:
CACHE n:預載 n 個值至記憶體,提升效能;崩潰會導致跳號。關鍵業務建議 NOCACHECYCLE:到達最大值後循環,PK 必須 NOCYCLEORDER:RAC 環境保證跨節點有序,單機可 NOORDER 提升效能CREATE OR REPLACE TRIGGER tri_test_id
BEFORE INSERT ON s_depart
FOR EACH ROW
DECLARE
v_next_id NUMBER;
BEGIN
IF :new.depart_id IS NULL OR :new.depart_id = 0 THEN
SELECT s_s_depart.NEXTVAL INTO v_next_id FROM DUAL;
:new.depart_id := v_next_id;
END IF;
END;
/
關鍵點:
BEFORE INSERT:在寫入前賦值:new.depart_id:參考新列值INSERT INTO s_depart (depart_name, depart_order) VALUES ('IT', 1);
INSERT INTO s_depart (depart_id, depart_name, depart_order) VALUES (100, 'HR', 2); -- 指定值
INSERT INTO s_depart (depart_name, depart_order) VALUES ('Finance', 3);
SELECT * FROM s_depart ORDER BY depart_id;
-- 結果:1 IT, 3 Finance, 100 HR
CREATE TABLE s_depart_modern (
depart_id NUMBER GENERATED ALWAYS AS IDENTITY,
depart_name NVARCHAR2(40) NOT NULL,
depart_order NUMBER DEFAULT 0,
CONSTRAINT pk_s_depart_modern PRIMARY KEY (depart_id)
);
等價於:
-- 內部自動建立序列 SYS_ISEQ$_xxxxxx
-- 內部自動建立隱藏觸發器
-- 允許指定值(相當於 BY DEFAULT)
depart_id NUMBER GENERATED BY DEFAULT AS IDENTITY
-- 起始值、間距、快取
depart_id NUMBER GENERATED ALWAYS AS IDENTITY
START WITH 1000 INCREMENT BY 10 CACHE 20
-- 查看序列定義
SELECT sequence_name, min_value, max_value, increment_by, cache_size, last_number
FROM user_sequences WHERE sequence_name = 'S_S_DEPART';
-- 修改序列(不可改 START WITH,需 DROP/CREATE)
ALTER SEQUENCE s_s_depart INCREMENT BY 5 MAXVALUE 10000 CYCLE NOCACHE;
-- 取得當前值/下一值
SELECT s_s_depart.CURRVAL FROM DUAL; -- 需先在 Session 呼叫過 NEXTVAL
SELECT s_s_depart.NEXTVAL FROM DUAL;
-- 刪除序列
DROP SEQUENCE s_s_depart;
| 問題 | 原因 | 解決 |
|---|---|---|
| 跳號 | CACHE 值在 Instance Crash 遺失 | 關鍵表用 NOCACHE;接受跳號為常態 |
| ORA-08002: sequence not yet defined | Session 未呼叫 NEXTVAL 就用 CURRVAL | 先執行 NEXTVAL 或改用 Identity |
| RAC 環境序列不連續 | 各節點各自 CACHE | 使用 ORDER 或 NOCACHE |
| 匯入後序列值小於表中最大值 | IMPDP 不自動對齊序列 | 匯入後手動 ALTER SEQUENCE ... RESTART START WITH (MAX(id)+1) |
| Trigger 遞迴錯誤 | Trigger 內再次 INSERT 同表 | 使用 WHEN (NEW.id IS NULL) 條件或複合觸發器 |
-- 清理舊物件
DROP TABLE s_depart PURGE;
DROP SEQUENCE s_s_depart;
DROP TRIGGER tri_test_id;
-- 傳統實作
CREATE TABLE s_depart (depart_id NUMBER NOT NULL, depart_name NVARCHAR2(40) NOT NULL, CONSTRAINT pk_s_depart PRIMARY KEY (depart_id));
CREATE SEQUENCE s_s_depart START WITH 1 NOCACHE;
CREATE OR REPLACE TRIGGER tri_test_id BEFORE INSERT ON s_depart FOR EACH ROW BEGIN IF :new.depart_id IS NULL THEN SELECT s_s_depart.NEXTVAL INTO :new.depart_id FROM DUAL; END IF; END;
/
-- 現代實作
CREATE TABLE s_depart_new (depart_id NUMBER GENERATED ALWAYS AS IDENTITY, depart_name NVARCHAR2(40) NOT NULL, CONSTRAINT pk_s_depart_new PRIMARY KEY (depart_id));
-- 測試
INSERT INTO s_depart (depart_name) VALUES ('Traditional');
INSERT INTO s_depart_new (depart_name) VALUES ('Modern');
SELECT * FROM s_depart;
SELECT * FROM s_depart_new;