PostgreSQL 12 提供了生成列(GENERATED ALWAYS AS STORED)功能,但是只能支持存儲型的生成列,需要占用存儲空間,更新成本高。
為此,PostgreSQL 18 即將引入一個新的增強:虛擬生成列。這種類型的字段值不需要存儲,而是在讀取數據時進行計算。虛擬生成列類似于視圖,而存儲生成列更像物化視圖。
我們可以通過在CREATE TABLE
或者ALTER TABLE
語句中指定字段的GENERATED ALWAYS AS
約束來創建一個生成列:
column_name data_type
GENERATED ALWAYS AS ( generation_expr ) [ STORED | VIRTUAL ]
其中,GENERATED ALWAYS AS
表示創建生成列;generation_expr 指定了生成列的表達式;STORED
表示存儲生成列,VIRTUAL
代表虛擬生成列(默認值)。
例如以下語句:
CREATE TABLE t_circle(id INTEGER PRIMARY KEY,x NUMERIC NOT NULL,y NUMERIC NOT NULL,radius NUMERIC NOT NULL,perimeter NUMERIC GENERATED ALWAYS AS (2 * 3.14159265 * radius) VIRTUAL
);ALTER TABLE t_circle ADD area NUMERIC GENERATED ALWAYS AS (3.14159265 * radius * radius) VIRTUAL;
首先,CREATE TABLE
語句為表 t_circle 定義了一個生成列 perimeter,表示圓的周長。然后,使用ALTER TABLE
語句增加一個生成列 area ,表示圓的面積。
接下來我們插入一些數據:
INSERT INTO t_circle VALUES (1, 2, 2, 5);
SELECT * FROM t_circle;
id|x|y|radius|perimeter |area |
--|-|-|------|-----------|-----------|1|2|2| 5|31.41592650|78.53981625|INSERT INTO t_circle(id, x, y, radius ,perimeter) VALUES (2, 0, 0, 1, 6.28318530);
SQL Error [42601]: ERROR: cannot insert into column "perimeter"Detail: Column "perimeter" is a generated column.
第一個插入語句沒有指定生成列的值,由數據庫自動計算;第二個插入語句為 perimeter 提供了數據,執行失敗;INSERT
和UPDATE
語句不能為生成列指定值。
虛擬生成列具有查詢時實時計算,不占用存儲空間等優點;但是目前 PostgreSQL 18 提供的虛擬生成列還存在一些限制:
- 不支持索引,包括唯一約束;
- 不支持擴展統計信息;
- 不支持外鍵約束;
- 不支持非空約束(可以使用檢查約束);
- 不支持 ALTER TABLE / DROP EXPRESSION 語句;
- 不支持域類型;
- 不支持邏輯復制。