生成列怎么用
生成列的值由其他列计算得出,写入时不用也不该手动提供。MySQL 有虚拟列与存储列两种:虚拟列不落盘、查询时计算,可以建二级索引;存储列把结果落盘,读取更快但占空间。PostgreSQL 目前只支持存储形式。常见用法是把表达式固化成列,再配索引服务固定的查询模式。
基本信息
| MySQL 虚拟列 | 默认 VIRTUAL,不落盘,查询时计算,可建二级索引 |
|---|---|
| MySQL 存储列 | STORED,结果落盘,可建索引,占空间 |
| PostgreSQL | 目前仅支持 STORED,表达式必须是不可变函数 |
| 写入方式 | 只能写被依赖的源列,直接写生成列会报错 |
| 典型用途 | 把表达式固化成列并加索引 |
典型使用场景
- 对 JSON 字段派生出的值建索引
- 把订单金额换算成含税金额固化下来
- 从时间戳派生日、年等维度列
- 把复杂表达式固化成列以便索引命中
- 让报表固定口径,不必每次重算
MySQL 写法
MySQL 写法
-- 存储列:结果落盘,可建索引
ALTER TABLE orders
ADD COLUMN total_with_tax DECIMAL(12,2)
GENERATED ALWAYS AS (total * 1.13) STORED;
-- 虚拟列加二级索引:列本身不落盘,索引里存值
ALTER TABLE orders
ADD COLUMN order_year INT GENERATED ALWAYS AS (YEAR(created_at)) VIRTUAL,
ADD INDEX idx_year (order_year);PostgreSQL 写法
PostgreSQL 写法
-- 只支持 STORED 形式,表达式须为不可变函数
ALTER TABLE orders
ADD COLUMN total_with_tax numeric(12,2)
GENERATED ALWAYS AS (total * 1.13) STORED;
-- 查看哪些列是生成列
SELECT attname, attgenerated
FROM pg_attribute
WHERE attrelid = 'orders'::regclass AND attgenerated <> '';两种生成列对照
| 对比项 | 虚拟列(MySQL) | 存储列 |
|---|---|---|
| 是否落盘 | 不落盘,查询时计算 | 落盘,占空间 |
| 读取成本 | 每次查询都要计算 | 直接读,成本低 |
| 能否建索引 | 可以(索引存值) | 可以 |
| 表达式变更 | 立即生效 | 需重建才能反映新公式 |
| 适用场景 | 计算轻、想省空间 | 计算重、读多写少 |
改表达式不会自动重算已有数据:存储列需要重建表或批量更新才能换成新公式的结果,改之前先评估数据量与窗口期。
表达式必须是确定性的:依赖当前时间或随机数的表达式在建列时就会被拒绝,需要这种取值就在写入时算好存进普通列。