一个UNION 类型(不要与SQL中的UNION操作符混淆)是一种能够容纳多个“替代”值之一的嵌套类型,类似于C语言中的union。主要区别在于这些UNION类型是标记联合,因此总是携带一个“标签”来指示当前持有的替代值,即使内部值本身为空。UNION类型因此更类似于C++17的std::variant、Rust的Enum或大多数函数式语言中的“和类型”。
UNION 类型必须始终至少有一个成员,虽然它们可以包含多个相同类型的成员,但标签名称必须是唯一的。UNION 类型最多可以有 256 个成员。
在底层,UNION 类型是在 STRUCT 类型的基础上实现的,并且简单地将“标签”作为第一个条目。
UNION 值可以通过 union_value(tag := expr) 函数创建,或者通过 从成员类型转换 来创建。
Example
创建一个带有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 |
| 二 |
| 三 |
选择所有的str成员:
SELECT union_extract(u, 'str') AS str
FROM tbl1;
| 字符串 |
|---|
| NULL |
| 二 |
| 三 |
或者,您可以像使用STRUCTs一样使用“点语法”。
SELECT u.str
FROM tbl1;
| 字符串 |
|---|
| NULL |
| 二 |
| 三 |
从UNION中选择当前活动的标签作为ENUM。
SELECT union_tag(u) AS t
FROM tbl1;
| t |
|---|
| num |
| str |
| str |
联合类型转换
与其他嵌套类型相比,UNION允许一组隐式转换,以便在将其成员作为“子类型”处理时,能够实现无侵入且自然的使用。
然而,这些转换在设计时遵循了两个原则,以避免歧义和可能导致信息丢失的转换。这防止了UNION完全“透明”,同时仍然允许UNION类型与其成员之间存在“超类型”关系。
因此,UNION 类型通常不能隐式转换为任何其成员类型,因为不匹配目标类型的其他成员中的信息将会“丢失”。如果你想将 UNION 强制转换为其中一个成员,你应该明确使用 union_extract 函数。
唯一的例外是将UNION转换为VARCHAR时,在这种情况下,所有成员都将使用其对应的VARCHAR转换。由于所有内容都可以转换为VARCHAR,这在某种意义上是“安全”的。
转换为联合类型
如果一个类型可以隐式转换为UNION的某个成员类型,那么它总是可以隐式转换为UNION。
- 如果有多个候选者,内置的隐式转换优先级规则将决定目标类型。例如,
FLOAT→UNION(i INTEGER, v VARCHAR)转换将始终在VARCHAR之前将FLOAT转换为INTEGER成员。 - 如果转换仍然不明确,即存在多个具有相同隐式转换优先级的候选者,则会引发错误。这通常发生在
UNION包含多个相同类型的成员时,例如,FLOAT→UNION(i INTEGER, num INTEGER)总是模棱两可的。
那么,如果我们想创建一个具有多个相同类型成员的UNION,我们该如何消除歧义呢?通过使用union_value函数,该函数接受一个指定标签的关键字参数。例如,union_value(num := 2::INTEGER)将创建一个具有单个INTEGER类型成员且标签为num的UNION。然后,这可以用于在显式(或隐式,见下文!)UNION到UNION的转换中消除歧义,例如CAST(union_value(b := 2) AS UNION(a INTEGER, b INTEGER))。
联合类型之间的转换
UNION 类型可以在彼此之间进行转换,前提是源类型是目标类型的“子集”。换句话说,源 UNION 中的所有标签都必须存在于目标 UNION 中,并且所有匹配标签的类型在源和目标之间必须可以隐式转换。本质上,这意味着 UNION 类型相对于其成员是协变的。
| 确定 | 来源 | 目标 | 评论 |
|---|---|---|---|
| ✅ | 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类型实现的,它们可以与所有比较运算符一起使用,也可以在WHERE和HAVING子句中使用,具有与STRUCT相同的语义。“标签”始终存储为第一个结构体条目,这确保了UNION类型首先按“标签”进行比较和排序。
Functions
参见 Union Functions。