⌘+k ctrl+k
1.4 (LTS)
搜索快捷键 cmd + k | ctrl + k
Union 类型

UNION 类型(不要与 SQL 的 UNION 运算符混淆)是一种嵌套类型,能够保存多个“备选”值中的一个,非常类似于 C 语言中的 union。其主要区别在于,这些 UNION 类型是带标签的联合(tagged unions),因此总是携带一个辨别“标签”,用于指示当前保存的是哪个备选值,即使内部值本身为空(null)也是如此。因此,UNION 类型更类似于 C++17 的 std::variant、Rust 的 Enum 或大多数函数式编程语言中的“求和类型(sum type)”。

UNION 类型必须至少包含一个成员,并且虽然它们可以包含多个相同类型的成员,但标签名称必须唯一。UNION 类型最多可以有 256 个成员。

在底层,UNION 类型是在 STRUCT 类型之上实现的,并简单地将“标签”作为第一个条目保存。

UNION 值可以使用 union_value(tag := expr) 函数创建,或者通过从成员类型转换来创建。

示例

创建带有 UNION 列的表

CREATE TABLE tbl1 (u UNION(num INTEGER, str VARCHAR));
INSERT INTO tbl1 VALUES (1), ('two'), (union_value(str := 'three'));

任何类型都可以隐式转换为包含该类型的 UNION。如果源 UNION 的成员是目标 UNION 成员的子集(且转换无歧义),则任何 UNION 也可以隐式转换为另一个 UNION

UNION 在转换为 VARCHAR 时会使用成员类型的 VARCHAR 转换函数

SELECT u FROM tbl1;
u
1
two
three

选择所有 str 成员

SELECT union_extract(u, 'str') AS str
FROM tbl1;
str
NULL
two
three

或者,你可以像使用 STRUCT 那样使用“点语法”。

SELECT u.str
FROM tbl1;
str
NULL
two
three

UNION 中当前激活的标签作为 ENUM 选择出来。

SELECT union_tag(u) AS t
FROM tbl1;
t
num
str
str

Union 转换

与其他嵌套类型相比,UNION 允许一组隐式转换,以便在将成员作为“子类型”使用时,操作更自然且具有非侵入性。然而,这些转换的设计遵循了两个原则:避免歧义和避免可能导致信息丢失的转换。这防止了 UNION 变得完全“透明”,同时仍允许 UNION 类型与其成员之间保持“超类型”关系。

因此,UNION 类型通常不能隐式转换为其任何成员类型,因为不匹配目标类型的其他成员中的信息将会“丢失”。如果你想将 UNION 强制转换为其成员之一,你应该显式使用 union_extract 函数。

唯一的例外是将 UNION 转换为 VARCHAR,在这种情况下,所有成员都将使用它们各自对应的 VARCHAR 转换。由于所有内容都可以转换为 VARCHAR,这在某种意义上是“安全”的。

转换为 Union

如果一个类型可以隐式转换为 UNION 的成员类型之一,那么它总是可以隐式转换为该 UNION

  • 如果有多个候选类型,内置的隐式转换优先级规则将决定目标类型。例如,FLOATUNION(i INTEGER, v VARCHAR) 转换将始终在 VARCHAR 之前将 FLOAT 转换为 INTEGER 成员。
  • 如果转换仍然存在歧义(即存在具有相同隐式转换优先级的多个候选),则会引发错误。这通常发生在 UNION 包含多个相同类型的成员时,例如 FLOATUNION(i INTEGER, num INTEGER) 始终是有歧义的。

那么,如果我们想创建一个具有多个相同类型成员的 UNION,该如何消除歧义呢?通过使用 union_value 函数,它接受一个指定标签的关键字参数。例如,union_value(num := 2::INTEGER) 将创建一个包含单个 INTEGER 类型成员且标签为 numUNION。这随后可用于在显式(或隐式,见下文!)UNIONUNION 转换中消除歧义,例如 CAST(union_value(b := 2) AS UNION(a INTEGER, b INTEGER))

Union 之间的转换

如果源类型是目标类型的“子集”,则 UNION 类型之间可以相互转换。换句话说,源 UNION 中的所有标签都必须存在于目标 UNION 中,并且所有匹配标签的类型在源和目标之间必须可以隐式转换。本质上,这意味着 UNION 类型相对于其成员是协变的。

Ok 目标 注释
UNION(a A, b B) UNION(a A, b B, c C)  
UNION(a A, b B) UNION(a A, b C) 如果 B 可以隐式转换为 C
UNION(a A, b B, c C) UNION(a A, b B)  
UNION(a A, b B) UNION(a A, b C) 如果 B 不能隐式转换为 C
UNION(A, B, D) UNION(A, B, C)  

比较与排序

由于 UNION 类型在内部是在 STRUCT 类型之上实现的,因此它们可以与所有比较运算符一起使用,也可以在 WHEREHAVING 子句中使用,其语义与 STRUCT 相同。“标签”始终作为第一个结构条目存储,这确保了 UNION 类型首先按“标签”进行比较和排序。

函数

参见 Union 函数

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