枚举类型
PostgreSQL 枚举(enum)是数据库级别的固定值集合。它适合值数量少、含义稳定且需要由数据库强制约束的状态,例如 pending、paid、cancelled。
创建和使用
CREATE TYPE order_status AS ENUM ('pending', 'paid', 'cancelled');
CREATE TABLE orders (
order_id bigint PRIMARY KEY,
status order_status NOT NULL DEFAULT 'pending'
);
INSERT INTO orders (order_id, status)
VALUES (1, 'paid');
插入不在集合中的值会失败。枚举值区分大小写,'Paid' 与 'paid' 是不同字符串;代码和迁移脚本应统一命名风格。
排序规则
枚举的比较顺序是创建时声明的顺序:
SELECT 'pending'::order_status < 'paid'::order_status;
这意味着顺序必须反映业务排序,否则 ORDER BY status 的结果会令人意外。枚举标签本身不是展示文案,界面仍应通过翻译表或应用字典显示友好名称。
添加、重命名和删除值
ALTER TYPE order_status ADD VALUE 'refunded' AFTER 'paid';
ALTER TYPE order_status RENAME VALUE 'cancelled' TO 'voided';
新增值通常比删除值容易。枚举值不能直接删除;删除或替换通常需要新建类型、转换列、删除旧类型,迁移过程要考虑锁和长事务。生产迁移前先检查 PostgreSQL 版本对事务中 ADD VALUE 的限制和行为。
如果状态经常变化、需要停用而不是删除、需要描述字段或多语言名称,使用字典表更灵活:
CREATE TABLE order_statuses (
code text PRIMARY KEY,
label text NOT NULL,
sort_order integer NOT NULL,
active boolean NOT NULL DEFAULT true
);
枚举与 CHECK 的取舍
简单且稳定的集合可以用 text + CHECK:
status text NOT NULL
CHECK (status IN ('pending', 'paid', 'cancelled'))
CHECK 的优点是迁移轻量、跨数据库更容易;枚举的优点是类型安全、集合可被系统元数据发现,并且不会把任意文本混入同一列。选型取决于变更频率和跨库需求,而不是性能传闻。
类型转换和查询
枚举与 text 混合运算时经常需要显式转换:
SELECT status::text
FROM orders
WHERE status = 'paid'::order_status;
不要在每个查询中把枚举转成文本再过滤;直接比较枚举值通常更清晰。为按状态查询建立索引前,先确认状态选择性和查询频率。
常见误区
- 把枚举当作可随意修改的配置表,导致删除和重排困难。
- 依赖声明顺序作为业务优先级,却在后续新增值时忘记检查排序。
- 把展示文案直接设计成枚举值,无法支持多语言和改名。
- 状态变化频繁仍使用枚举,迁移成本超过了类型安全收益。