- 安装
- 文档
- 入门
- 连接
- 数据导入
- 客户端 API
- 概览
- ADBC
- C
- C++
- CLI
- Dart
- Go
- Java (JDBC)
- Julia
- Node.js (已弃用)
- Node.js (Neo)
- ODBC
- PHP
- Python
- R
- Rust
- Swift
- Wasm
- SQL
- 介绍
- 语句
- 概览
- ANALYZE
- ALTER TABLE
- ALTER VIEW
- ATTACH 和 DETACH
- CALL
- CHECKPOINT
- COMMENT ON
- COPY
- CREATE INDEX
- CREATE MACRO
- CREATE SCHEMA
- CREATE SECRET
- CREATE SEQUENCE
- CREATE TABLE
- CREATE VIEW
- CREATE TYPE
- DELETE
- DESCRIBE
- DROP
- EXPORT 和 IMPORT DATABASE
- INSERT
- LOAD / INSTALL
- PIVOT
- 性能分析
- SELECT
- SET / RESET
- SET VARIABLE
- SUMMARIZE
- 事务管理
- UNPIVOT
- UPDATE
- USE
- VACUUM
- 查询语法
- SELECT
- FROM 和 JOIN
- WHERE
- GROUP BY
- GROUPING SETS
- HAVING
- ORDER BY
- LIMIT 和 OFFSET
- SAMPLE
- 展开嵌套
- WITH
- WINDOW
- QUALIFY
- VALUES
- FILTER
- 集合操作
- 预处理语句
- 数据类型
- 表达式
- 函数
- 概览
- 聚合函数
- 数组函数
- 位字符串函数
- Blob 函数
- 日期格式化函数
- 日期函数
- 日期部分函数
- 枚举函数
- 间隔函数
- Lambda 函数
- 列表函数
- 映射函数
- 嵌套函数
- 数值函数
- 模式匹配
- 正则表达式
- 结构体函数
- 文本函数
- 时间函数
- 时间戳函数
- 带时区时间戳函数
- 联合函数
- 实用函数
- 窗口函数
- 约束
- 索引
- 元查询
- DuckDB 的 SQL 方言
- 示例
- 配置
- 扩展
- 核心扩展
- 概览
- 自动补全
- Avro
- AWS
- Azure
- Delta
- DuckLake
- 编码
- Excel
- 全文搜索
- httpfs (HTTP 和 S3)
- Iceberg
- ICU
- inet
- jemalloc
- MySQL
- PostgreSQL
- 空间
- SQLite
- TPC-DS
- TPC-H
- UI
- VSS
- 指南
- 概览
- 数据查看器
- 数据库集成
- 文件格式
- 概览
- CSV 导入
- CSV 导出
- 直接读取文件
- Excel 导入
- Excel 导出
- JSON 导入
- JSON 导出
- Parquet 导入
- Parquet 导出
- 查询 Parquet 文件
- 使用 file: 协议访问文件
- 网络和云存储
- 概览
- HTTP Parquet 导入
- S3 Parquet 导入
- S3 Parquet 导出
- S3 Iceberg 导入
- S3 Express One
- GCS 导入
- Cloudflare R2 导入
- 通过 HTTPS / S3 使用 DuckDB
- Fastly 对象存储导入
- 元查询
- ODBC
- 性能
- Python
- 安装
- 执行 SQL
- Jupyter Notebooks
- marimo Notebooks
- Pandas 上的 SQL
- 从 Pandas 导入
- 导出到 Pandas
- 从 Numpy 导入
- 导出到 Numpy
- Arrow 上的 SQL
- 从 Arrow 导入
- 导出到 Arrow
- Pandas 上的关系型 API
- 多个 Python 线程
- 与 Ibis 集成
- 与 Polars 集成
- 使用 fsspec 文件系统
- SQL 编辑器
- SQL 功能
- 代码片段
- 故障排除
- 术语表
- 离线浏览
- 操作手册
- 开发
- 内部结构
- 为什么选择 DuckDB
- 行为准则
- 发布日历
- 路线图
- 站点地图
- 在线演示
联合类型(不要与SQL UNION
运算符混淆)是一种嵌套类型,能够容纳多个“替代”值之一,非常类似于C语言中的union
。主要区别在于这些UNION
类型是带标签的联合,因此总是带有一个判别器“标签”,该标签指示它当前持有的替代值,即使内部值本身为空。因此,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
。
当转换为VARCHAR
时,UNION
使用成员类型的VARCHAR
转换函数
SELECT u FROM tbl1;
u |
---|
1 |
二 |
三 |
选择所有str
成员
SELECT union_extract(u, 'str') AS str
FROM tbl1;
str |
---|
NULL |
二 |
三 |
或者,您可以像对STRUCT
类型一样使用“点语法”。
SELECT u.str
FROM tbl1;
str |
---|
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)
的类型转换将始终优先将FLOAT
转换为INTEGER
成员,而不是VARCHAR
。 - 如果类型转换仍然存在歧义,即有多个具有相同隐式转换优先级的候选类型,则会引发错误。这通常发生在
UNION
包含多个相同类型的成员时,例如,FLOAT
→UNION(i INTEGER, num INTEGER)
总是存在歧义。
那么,如果我们想创建包含多个相同类型成员的UNION
,该如何消除歧义呢?通过使用union_value
函数,它接受一个指定标签的关键字参数。例如,union_value(num := 2::INTEGER)
将创建一个UNION
,其中包含一个类型为INTEGER
的成员,标签为num
。然后,这可以在显式(或隐式,请继续阅读下文!)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
类型首先按“标签”进行比较和排序。
函数
参见联合函数。