HOWTO · PostgreSQL

PostgreSQL 中的自動遞增值

本文介紹如何在 PostgreSQL 中自動遞增值。

本頁內容

PostgreSQL 不使用 MySQL 的 AUTO_INCREMENT 欄位語法。若要自動產生識別碼,大多數新資料表建議使用 identity 欄位;也可以使用較舊的 SERIAL 系列,或使用搭配 nextval 的序列。這些機制會提供欄位值,但本身不保證唯一性。若需要拒絕重複識別碼,請加上 PRIMARY KEY 或 UNIQUE 限制。

新資料表使用 Identity 欄位

identity 欄位由關聯的序列提供值。若新增資料列時可能會自行指定識別碼,請將欄位宣告為 GENERATED BY DEFAULT AS IDENTITY。若通常應由 PostgreSQL 提供欄位值,並拒絕呼叫端自行指定的值,請使用 GENERATED ALWAYS AS IDENTITY。新增資料列時,省略 identity 欄位或指定 DEFAULT,這兩種形式都會產生值。

請先用完整陳述式建立資料表,再省略 identity 欄位來新增資料列:

CREATE TABLE cat (id integer GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, color varchar NOT NULL);

INSERT INTO cat (color) VALUES ('black'), ('white'), ('brown'), ('tuxedo');

由於省略了 id,PostgreSQL 會為每一列提供一個值。實際的起始值取決於 identity 序列的設定;若要取得特定新增操作所指派的識別碼,請使用 RETURNING id。這些 SQL 陳述式僅供說明,未在此處的 PostgreSQL 伺服器上執行。

BY DEFAULT 允許在新增資料列時指定識別碼;ALWAYS 則可避免產生的值遭到意外取代。兩者的差異在於如何處理明確指定的值,而非產生的值是否唯一。此例中的主鍵會確保唯一性。

下面的小型 Python 模型示範呼叫端省略識別碼時如何指派下一個值。這只是模型,不會連線至 PostgreSQL,也不會重現資料庫序列的行為。

執行此範例:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

next_id = 1
rows = []
for color in ["black", "white", "brown", "tuxedo"]:
    rows.append((next_id, color))
    next_id += 1
print(rows)

使用 SERIAL 或序列

SERIAL 和 BIGSERIAL 是較精簡、較舊的 identity 欄位替代方案。SERIAL 會建立一個預設值取自關聯序列的整數欄位;BIGSERIAL 的作用相同,但使用較大的整數型別。它們是在資料表定義中使用的虛擬型別,不是真正的整數型別。SERIAL 使用 4 位元組整數,BIGSERIAL 則使用 8 位元組整數。序列不會自動將欄位設為主鍵;如有需要,請加上主鍵或唯一性限制。

對於 cat 資料表,id SERIAL PRIMARY KEY 和 color varchar NOT NULL 會定義一個自動產生的識別碼,並以主鍵限制其唯一性。只指定 color 的新增操作(例如前面的範例)會取得序列值。序列值可能出現間隔:已被回復交易使用,或被後續失敗的新增操作消耗的值都不會重複使用。請勿將序列值當成沒有間隔的資料列計數。

如果需要直接設定或共用序列行為,可以明確建立序列。例如,CREATE SEQUENCE cat_id_seq START WITH 100; 會建立一個從 100 開始的序列,而 id integer NOT NULL DEFAULT nextval('cat_id_seq') 會使用序列的下一個值。nextval 會推進序列;它不是隨機產生器。手動建立序列時,也要考慮其擁有關係與生命週期:ALTER SEQUENCE cat_id_seq OWNED BY cat.id; 會將序列關聯至欄位,讓它能隨該欄位一起管理。

在 Identity 欄位中新增明確指定的值

使用 GENERATED BY DEFAULT AS IDENTITY 時,該資料列會採用明確指定的識別碼。使用 GENERATED ALWAYS AS IDENTITY 時,PostgreSQL 通常會拒絕明確指定的值。若要刻意載入指定值,請使用 OVERRIDING SYSTEM VALUE,例如:INSERT INTO cat (id, color) OVERRIDING SYSTEM VALUE VALUES (20, 'black');。這個子句會告訴 PostgreSQL,在這次新增操作中使用指定的值。

明確指定的值不會自動推進 identity 欄位的關聯序列。如果手動新增的值可能與之後產生的值重複,請在恢復自動產生值之前調整序列。一般新增資料列時,請省略 identity 欄位,讓 PostgreSQL 選擇值。請使用 RETURNING id 取得產生的識別碼,不要預測下一個值;並行交易和序列值間隔都會使預測不可靠。

為現有資料表加入自動產生的值

對現有資料表,可以使用 ALTER TABLE cat ADD COLUMN id integer GENERATED BY DEFAULT AS IDENTITY; 新增 identity 欄位。新增欄位時,PostgreSQL 會為資料列提供 identity 值,但這個陳述式不會將欄位設為主鍵。只有在確認資料符合主鍵限制,且資料表尚未有其他主鍵之後,才新增主鍵。

若要為現有的一般欄位設定由序列提供的預設值,可以先建立序列,再設定欄位預設值,例如先執行 CREATE SEQUENCE cat_id_seq;,接著執行 ALTER TABLE cat ALTER COLUMN id SET DEFAULT nextval('cat_id_seq');。這只會影響省略 id 的後續新增操作;它不會填入既有的空值,也不會確保既有值唯一。在依賴自動產生的值之前,請將序列初始化至高於資料表目前的最大識別碼,並一併考量資料表是否為空,以及預期的起始值。若應用程式需要這些保證,接著請加上 NOT NULL 或唯一性限制。一般的自動識別碼行為可選用 identity;若要沿用現有的序列設計,或需要直接設定序列,則可使用 SERIAL 或明確建立序列。