在設計資料庫表時,您通常需要一個唯一識別碼作為主鍵,例如使用者ID、訂單號等。手動管理這些ID既繁瑣又容易出錯。為瞭解決這一問題,PolarDB PostgreSQL版(相容Oracle)提供了序列和自動產生列兩種機制。它們都能自動、安全地產生唯一的數字序列,簡化了主鍵管理,並確保了資料的一致性。
功能簡介
PolarDB PostgreSQL版(相容Oracle)提供了多種產生唯一序列值的方式,以滿足不同情境的需求,並高度相容Oracle資料庫。
傳統序列
這是一個獨立的資料庫物件,通過CREATE SEQUENCE建立。在插入資料時,您需要通過調用NEXTVAL偽列(例如my_seq.NEXTVAL)顯式地從序列中擷取下一個值並插入到表中。這種方式最靈活,序列和表是解耦的。自動產生列:
這是Oracle 12c引入的特性,它將序列隱式地與表中的某一資料行繫結。在建立表時,您只需在列定義上添加GENERATED ... AS IDENTITY子句,資料庫就會在插入新行時自動為該列產生值,無需在INSERT語句中顯式處理。這種方式更加便捷,符合現代資料庫的設計趨勢。
自動產生列根據其行為策略,又分為以下三種類型:
GENERATED ALWAYS AS IDENTITY:強制自動產生。系統總是為該列產生值,您不能在INSERT語句中為該列提供顯式值,否則會報錯。這種策略最嚴格,能確保ID完全由資料庫控制。GENERATED BY DEFAULT AS IDENTITY:預設自動產生。只有在INSERT語句中未指定該列時,系統才會為其產生值。如果您提供了具體值,系統將使用您提供的值。但如果您顯式提供NULL,則會報錯。GENERATED BY DEFAULT ON NULL AS IDENTITY:預設或在NULL時自動產生。這是最靈活的策略。當INSERT語句中未指定該列,或為該列顯式提供了NULL值時,系統都會自動為其產生值。這種方式在資料移轉或需要混合插入自訂ID和自動產生ID的情境中非常有用。
前提條件
您的PolarDB PostgreSQL版(相容Oracle)叢集的修訂版本需為2.0.14.17.36.0及以上
優勢
簡化開發:自動產生列將ID產生的邏輯封裝在表定義中,使
INSERT語句更簡潔,無需關心序列的調用。保證唯一性:由資料庫保證產生的序列值是唯一的,避免了應用程式層產生ID可能導致的衝突。
靈活性強:
GENERATED BY DEFAULT ON NULL策略兼顧了自動產生和手動指定的雙重需求,特別適合資料匯入和歷史資料移轉等複雜情境。
選型建議
為了協助您更好地選擇,下表對比了不同序列產生方式的特性:
產生方式 | 手動指定值 | 顯式插入NULL | 靈活性 | 資料移轉友好度 | 推薦情境 |
傳統序列 ( | 支援 | 支援(列需允許NULL) | 最高 | 高 | 與多表共用序列。 |
強制產生 ( | 不支援 | 不適用 | 最低 | 低 | 嚴格自增,禁止幹預。 |
預設產生 ( | 支援 | 不支援(報錯) | 中 | 中 | 允許覆蓋ID,但不允許NULL。 |
預設或NULL時產生 ( | 支援 | 支援(觸發自動產生) | 高 | 最高 | 新應用、資料移轉、混合ID情境。 |
最佳實務
對於新開發的應用,推薦使用
GENERATED BY DEFAULT ON NULL AS IDENTITY,因為它提供了最大的靈活性。在進行資料移轉或需要匯入包含存量ID的資料時,
GENERATED BY DEFAULT ON NULL是理想選擇。如果業務要求ID必須由資料庫嚴格控制且絕不允許手動幹預,請使用
GENERATED ALWAYS AS IDENTITY。在高並發插入情境下,為
IDENTITY列或傳統序列設定一個合理的CACHE值(如CACHE 20或更高)可以顯著提升效能。
使用傳統序列(Sequence)
這種方式下,序列和表是兩個獨立的對象。
建立序列
首先,使用
CREATE SEQUENCE命令建立一個序列對象,並可以自訂其起始值、步長、最大/小值等屬性。CREATE SEQUENCE emp_seq START WITH 1000 -- 起始值為1000 INCREMENT BY 1 -- 每次遞增1 MAXVALUE 9999 -- 最大值為9999 NOCYCLE -- 不迴圈 CACHE 20; -- 緩衝20個值以提高效能參數說明
START WITH:起始值。INCREMENT BY:增量步長。MAXVALUE/NOMAXVALUE:最大值。MINVALUE/NOMINVALUE:最小值。CYCLE/NOCYCLE:是否迴圈。CACHE/NOCACHE:緩衝數量。ORDER/NOORDER:是否保證順序。
在
INSERT語句中使用序列在向表中插入資料時,通過調用序列的
NEXTVAL偽列來擷取新的ID。-- 建立一個使用序列的表 CREATE TABLE employees ( emp_id NUMBER(10) PRIMARY KEY, emp_name VARCHAR2(50) NOT NULL ); -- 插入資料時調用emp_seq.NEXTVAL INSERT INTO employees (emp_id, emp_name) VALUES (emp_seq.NEXTVAL, 'John Smith'); INSERT INTO employees (emp_id, emp_name) VALUES (emp_seq.NEXTVAL, 'Jane Doe'); -- 查詢結果 SELECT * FROM employees; -- emp_id | emp_name -- --------+------------ -- 1000 | John Smith -- 1001 | Jane Doe查詢結果
SELECT * FROM employees;返回結果:
emp_id | emp_name --------+------------ 1000 | John Smith 1001 | Jane Doe (2 rows)管理序列
您可以查詢
user_sequences視圖來查看序列的目前狀態,或使用ALTER SEQUENCE修改其屬性。-- 查看序列的當前值(需要先調用一次NEXTVAL) SELECT emp_seq.CURRVAL FROM dual; -- 修改序列的步長和緩衝 ALTER SEQUENCE emp_seq INCREMENT BY 2 CACHE 50;
使用自動產生列(Identity Column)
這種方式將序列與表列直接綁定,更為現代化和便捷。
強制產生(GENERATED ALWAYS)
適用於所有ID都應由資料庫嚴格控制的情境。
建立表:建立一個帶有強制自動產生列的表,並插入資料。
-- 建立一個帶有強制自動產生列的表 CREATE TABLE products ( product_id NUMBER GENERATED ALWAYS AS IDENTITY ( START WITH 100 INCREMENT BY 10 ), product_name VARCHAR2(100) NOT NULL ); -- 插入資料時,無需也不能指定product_id列 INSERT INTO products (product_name) VALUES ('Laptop'); INSERT INTO products (product_name) VALUES ('Mouse');嘗試顯式插入ID。
INSERT INTO products (product_id, product_name) VALUES (999, 'Keyboard');返回結果:報錯。
ERROR: cannot insert a non-DEFAULT value into column "product_id" DETAIL: Column "product_id" is an identity column defined as GENERATED ALWAYS.查詢結果
SELECT * FROM products ORDER BY product_id;返回結果:
product_id | product_name ------------+-------------- 100 | Laptop 110 | Mouse (2 rows)
預設產生(GENERATED BY DEFAULT)
此方式允許您在需要時覆蓋自動產生的ID,但與ON NULL不同,它不允許顯式插入NULL。
建立表。
CREATE TABLE transactions ( trans_id NUMBER GENERATED BY DEFAULT AS IDENTITY, amount NUMBER(10,2) );插入方式1:不指定ID,自動產生。
INSERT INTO transactions (amount) VALUES (500.00);插入方式2:顯式插入一個自訂。
INSERT INTO transactions (trans_id, amount) VALUES (9999, 200.00);插入方式3:顯式插入NULL將會失敗。
INSERT INTO transactions (trans_id, amount) VALUES (NULL, 150.00);返回結果:報錯。
ERROR: null value in column "trans_id" of relation "transactions" violates not-null constraint DETAIL: Failing row contains (null, 150).查詢結果
SELECT * FROM transactions ORDER BY trans_id;返回結果:
trans_id | amount ----------+-------- 1 | 500 9999 | 200 (2 rows)
預設或在NULL時產生(GENERATED BY DEFAULT ON NULL)
這是最靈活的方式,推薦在新應用和資料移轉情境中使用。
建立表:建立一個帶有靈活自動產生列的表。
-- 建立一個帶有靈活自動產生列的表 CREATE TABLE customers ( customer_id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY, customer_name VARCHAR2(100) NOT NULL );插入方式1:不指定ID,由資料庫自動產生。
INSERT INTO customers (customer_name) VALUES ('John Doe');插入方式2:顯式插入一個自訂ID(例如,遷移歷史資料)。
INSERT INTO customers (customer_id, customer_name) VALUES (5000, 'Jane Smith');插入方式3:顯式插入NULL,由資料庫自動產生。
INSERT INTO customers (customer_id, customer_name) VALUES (NULL, 'Bob Wilson');查詢結果
SELECT * FROM customers ORDER BY customer_id;返回結果:
customer_id | customer_name -------------+--------------- 1 | John Doe 2 | Bob Wilson 5000 | Jane Smith (3 rows)
序列管理與維護
無論是傳統序列還是與自動產生列關聯的內部序列,您都可以對其進行管理和維護。
查看序列資訊
您可以查詢user_sequences視圖來監控所有使用者自訂序列的狀態。
SELECT sequence_name, min_value, max_value, increment_by,
last_number, cache_size, cycle_flag
FROM user_sequences;修改序列屬性
使用ALTER SEQUENCE命令可以修改序列的步長、最大值、緩衝大小等屬性。
-- 將emp_seq序列的步長改為2,最大值改為99999,緩衝改為50
ALTER SEQUENCE emp_seq
START WITH 1005
INCREMENT BY 2
MAXVALUE 99999
CACHE 50;重設序列
PolarDB PostgreSQL版(相容Oracle)不支援重設序列的命令。標準的做法是刪除並重新建立序列。
-- 刪除現有序列
DROP SEQUENCE emp_seq;
-- 重新建立序列,並設定新的起始值
CREATE SEQUENCE emp_seq START WITH 5000 INCREMENT BY 1;實際應用情境
在使用者註冊情境中,GENERATED BY DEFAULT ON NULL提供了極大的便利。它既能為新註冊使用者自動產生ID,也允許在資料移轉或大量匯入時指定歷史ID。
建立使用者賬戶表。
CREATE TABLE user_accounts ( user_id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY ( START WITH 100000 CACHE 100 ), username VARCHAR2(50) UNIQUE NOT NULL, email VARCHAR2(100) UNIQUE NOT NULL, created_date DATE DEFAULT SYSDATE );情境1:新使用者正常註冊,自動產生ID。
INSERT INTO user_accounts (username, email) VALUES ('john_doe', 'john@example.com');情境2:從舊系統遷移資料,指定歷史ID。
INSERT INTO user_accounts (user_id, username, email, created_date) VALUES (99999, 'legacy_user', 'leg***@system.com', DATE '2020-01-01');情境3:大量匯入,混合使用自動產生和指定ID。
INSERT ALL INTO user_accounts (username, email) VALUES ('alice', 'ali**@test.com') INTO user_accounts (user_id, username, email) VALUES (88888, 'system_acct', 'sys***@app.com') SELECT * FROM dual;查詢最終結果。
SELECT * FROM user_accounts ORDER BY user_id;返回結果:
user_id | username | email | created_date ---------+-------------+-------------------+--------------------- 88888 | system_acct | sys***@app.com | 2025-09-17 10:21:38 99999 | legacy_user | leg***@system.com | 2020-01-01 00:00:00 100000 | john_doe | john@example.com | 2025-09-17 10:21:38 100001 | alice | ali**@test.com | 2025-09-17 10:21:38 (4 rows)