⌘+k ctrl+k
1.4 (LTS)
搜索快捷键 cmd + k | ctrl + k
枚举(Enum)数据类型
名称 描述
ENUM 表示某一列所有可能字符串值的字典

枚举类型代表了一种字典数据结构,其中包含了某一列所有可能的唯一值。例如,存储星期几的列可以使用枚举来保存所有可能的日期。枚举对于基数较低(即不同值较少)的字符串列特别有用。这是因为该列在枚举字典中仅存储指向字符串的数值引用,从而极大地节省了磁盘空间并提高了查询性能。

创建枚举

你可以使用硬编码的值来创建枚举

CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
-- This statement will fail since enums cannot hold NULL values:
-- CREATE TYPE mood AS ENUM ('sad', NULL);
-- This statement will fail since enum values must be unique:
-- CREATE TYPE mood AS ENUM ('sad', 'sad');

你可以在特定的模式(schema)中创建枚举

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

匿名枚举可以在类型转换(casting)过程中即时创建

SELECT 'clubs'::ENUM ('spades', 'hearts', 'diamonds', 'clubs');

你还可以使用返回单个 VARCHAR 列的 SELECT 语句来创建枚举。来自 SELECT 语句的值集合将自动去重,并且 NULL 值会被忽略

CREATE TYPE region AS ENUM (SELECT region FROM sales_data);

如果你正在从文件导入数据,可以在导入之前为 VARCHAR 列创建枚举

CREATE TYPE region AS ENUM (SELECT region FROM 'sales_data.csv');
CREATE TABLE sales_data (amount INTEGER, region region);
COPY sales_data FROM 'sales_data.csv';

使用枚举

枚举值区分大小写,因此 'maltese' 和 'Maltese' 被视为不同的值

CREATE TYPE breed AS ENUM ('maltese', 'Maltese');
-- Will return false
SELECT 'maltese'::breed = 'Maltese'::breed;
-- Will error
SELECT 'MALTESE'::breed;

枚举创建完成后,可以在使用标准内置类型的任何地方使用它。例如,我们可以创建一个包含引用该枚举的列的表。

CREATE TABLE person (
    name TEXT,
    current_mood mood
);
INSERT INTO person VALUES
    ('Pedro', 'happy'),
    ('Mark', NULL),
    ('Pagliacci', 'sad'),
    ('Mr. Mackey', 'ok');

以下查询将会失败,因为 mood 类型没有 quackity-quack 这个值。

INSERT INTO person VALUES ('Hannes', 'quackity-quack');

枚举 vs. 字符串

DuckDB 枚举在必要时会自动转换为 VARCHAR 类型。此特性允许在不同枚举之间,或枚举与 VARCHAR 列之间进行比较。

它还允许将枚举用于任何 VARCHAR 函数。例如

SELECT current_mood, regexp_matches(current_mood, '.*a.*') AS contains_a FROM person;
current_mood contains_a
happy true
NULL NULL
sad true
ok false

当比较两个不同的枚举类型时,DuckDB 会将两者都转换为字符串并执行字符串比较

CREATE TYPE new_mood AS ENUM ('happy', 'anxious');
SELECT * FROM person
WHERE current_mood = 'happy'::new_mood;
-- Equivalent to `WHERE current_mood::VARCHAR = 'happy'::VARCHAR`
name current_mood
Pedro happy

当比较枚举与 VARCHAR 时,DuckDB 会将枚举转换为 VARCHAR 并执行字符串比较

SELECT * FROM person
WHERE current_mood = name;
-- Equivalent to `WHERE current_mood::VARCHAR = name`
-- No rows returned

当与常量字符串进行比较时,DuckDB 会执行优化并进行 try_cast(constant string, enum_type) 操作,这样我们在物理层面执行的是整数比较而非字符串比较(但在逻辑上它仍然是字符串比较)

SELECT * FROM person
WHERE current_mood = 'sad';
-- Equivalent to `WHERE current_mood::VARCHAR = 'sad'`
name current_mood
Pagliacci sad

警告:这意味着与随机(非等效)字符串进行比较的结果始终为 false(并且不会报错)

SELECT * FROM person
WHERE current_mood = 'bogus';
-- Equivalent to `WHERE current_mood::VARCHAR = 'bogus'`
-- No rows returned

如果你想强制执行类型安全,请显式地转换为枚举类型

SELECT * FROM person
WHERE current_mood = 'bogus'::mood;
-- Conversion Error: Could not convert string 'bogus' to UINT8

枚举的排序

枚举值的排序遵循它们在枚举定义中的顺序。例如

CREATE TYPE priority AS ENUM ('low', 'medium', 'high');
SELECT 'low'::priority < 'high'::priority AS comp;
-- note that 'low'::VARCHAR < 'high'::VARCHAR is false!
comp
true
SELECT unnest(['medium'::priority, 'high'::priority, 'low'::priority]) AS m
ORDER BY m;
m
low
medium
high

警告:如果你将枚举与非枚举类型(例如 VARCHAR 或不同的枚举类型)进行比较,枚举将首先被转换为字符串(如上一节所述),比较将按照字符串的字典顺序进行

CREATE TABLE tasks (name TEXT, priority_level priority);
INSERT INTO tasks VALUES ('a', 'low'), ('b', 'medium'), ('c', 'high');
-- WARNING!
-- Equivalent to `WHERE priority_level::VARCHAR >= 'medium'`
SELECT * FROM tasks
WHERE priority_level >= 'medium';  
-- Misses the 'high' priority task!
name priority_level
b medium

因此,如果你想例如“获取所有等于或高于 medium 的优先级”,则需要显式地转换为枚举类型

SELECT * FROM tasks
WHERE priority_level >= 'medium'::priority;
name priority_level
b medium
c high

函数

请参阅 枚举函数

例如,使用 enum_range 函数显示 moods 枚举中可用的值

SELECT enum_range(NULL::moods) AS my_enum_range;
my_enum_range
[sad, ok, happy]

移除枚举

枚举类型存储在目录(catalog)中,并且会向使用它们的每个表添加一个目录依赖项。可以使用以下命令从目录中删除枚举

DROP TYPE enum_name;

目前,可以删除表中正在使用的枚举,而不会影响这些表。

警告:枚举移除功能的此行为可能会发生变化。在未来的版本中,预计在删除枚举之前必须移除任何相关的列,或者必须使用附加的 CASCADE 参数来删除枚举。

© 2025 DuckDB 基金会,阿姆斯特丹,荷兰
行为准则 商标使用指南