枚举
枚举(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,而且可能永远不会有。如果您需要一组非固定的键值,那么最好使用外键而不是枚举类型。