一个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 |
二 |
三 |
或者,您可以像使用STRUCT
s一样使用“点语法”。
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。