枚举

来自 PostgreSQL 维基
跳转到导航跳转到搜索

枚举(enum)类型是一种数据类型,它包含一组静态的、预定义的值,并且这些值具有特定的顺序。它们等同于许多编程语言中的 enum 类型。枚举类型的一个例子可能是星期几,或者一组数据的状态值。

查看枚举类型的官方文档

枚举类型的声明

枚举类型使用 CREATE TYPE 命令创建,例如

CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');

创建后,枚举类型可以在表和函数定义中使用,就像任何其他类型一样

示例。基本枚举用法

CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
CREATE TABLE person (
   name text,
   current_mood mood
);
INSERT INTO person VALUES ('Moe', 'happy');
SELECT * FROM person WHERE current_mood = 'happy';
 name | current_mood
------+--------------
 Moe  | happy
(1 row)

特性

排序

枚举类型中值的排序是声明类型时列出值的顺序。所有标准比较运算符和相关的聚合函数都支持枚举。例如

示例。枚举排序

INSERT INTO person VALUES ('Larry', 'sad');
INSERT INTO person VALUES ('Curly', 'ok');
SELECT * FROM person WHERE current_mood > 'sad';
name  | current_mood 
-------+--------------
Moe   | happy
Curly | ok
(2 rows)
SELECT * FROM person WHERE current_mood > 'sad' ORDER BY current_mood;
 name  | current_mood 
-------+--------------
 Curly | ok
 Moe   | happy
(2 rows)
SELECT name FROM person
 WHERE current_mood = (SELECT MIN(current_mood) FROM person);
 name  
-------
 Larry
(1 row)

类型安全

枚举类型是完全独立的数据类型,不能相互比较。

示例。缺乏强制转换

CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
CREATE TABLE holidays (                                           
   num_weeks int,
   happiness happiness
);
INSERT INTO holidays(num_weeks,happiness) VALUES (4, 'happy');
INSERT INTO holidays(num_weeks,happiness) VALUES (6, 'very happy');
INSERT INTO holidays(num_weeks,happiness) VALUES (8, 'ecstatic');
INSERT INTO holidays(num_weeks,happiness) VALUES (2, 'sad');
ERROR:  invalid input value for enum happiness: "sad"
SELECT person.name, holidays.num_weeks FROM person, holidays
 WHERE person.current_mood = holidays.happiness;
ERROR:  operator does not exist: mood = happiness

如果您确实需要执行类似的操作,您可以编写自定义运算符,或在查询中添加显式强制转换。

示例。通过强制转换为文本比较不同的枚举

SELECT person.name, holidays.num_weeks FROM person, holidays
 WHERE person.current_mood::text = holidays.happiness::text;
 name | num_weeks 
------+-----------
 Moe  |         4
(1 row)

实现细节

一个枚举值在磁盘上占用四个字节。枚举值文本标签的长度受编译到 PostgreSQL 中的 NAMEDATALEN 设置限制;在标准构建中,这意味着最多 63 个字节。

枚举标签区分大小写,因此 'happy' 与 'HAPPY' 不同。标签中的空格也很重要。

另一种实现方式

除了使用枚举类型,我们还可以设置 CHECK CONSTRAINT - 这告诉 postgresql 确保我们输入的值有效。

CREATE TABLE person (
 personid int not null primary key,
 favourite_colour varchar(255) NOT NULL,
 CHECK (favourite_colour IN ('red', 'blue', 'yellow', 'purple'))
);
INSERT INTO person(personid, favourite_colour) VALUES (1, 'red');
INSERT 0 1

现在来点不在列表中的内容

INSERT INTO person(personid, favourite_colour) VALUES (2, 'green');
ERROR: new row for relation "person" violates check constraint "person_favourite_colour_check"

常见问题解答

为什么我们无法删除 ENUM 值

我们无法删除一个值,因为它可能在许多地方使用:多个表、索引...

查看 Tom Lane 的此答案:https://postgresql.ac.cn/message-id/21012.1459434338%40sss.pgh.pa.us

不,这样做**不安全**。至少在您也删除或重新索引枚举类型列上的所有索引之前,这样做不安全。即使您已删除表行中出现的目标值的每个实例,并且使用 vacuum 移除这些行,使它们的叶索引项消失,目标值仍然可能存在于上级索引页面中(例如,作为页面边界值)。删除 pg_enum 项会破坏索引,因为 enum_cmp() 将不知道在访问该索引项时该怎么做。

类似的顾虑正是为什么没有 ALTER TYPE DELETE VALUE,而且可能永远不会有。如果您需要一组非固定的键值,那么最好使用外键而不是枚举类型。