| 名称 | 描述 |
|---|---|
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参数来删除枚举。