跳到主要内容

枚举类型

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;

不要在每个查询中把枚举转成文本再过滤;直接比较枚举值通常更清晰。为按状态查询建立索引前,先确认状态选择性和查询频率。

常见误区​

  • 把枚举当作可随意修改的配置表,导致删除和重排困难。
  • 依赖声明顺序作为业务优先级,却在后续新增值时忘记检查排序。
  • 把展示文案直接设计成枚举值,无法支持多语言和改名。
  • 状态变化频繁仍使用枚举,迁移成本超过了类型安全收益。