Тип объединения
Тип объединения (не путать с оператором SQL UNION) — это вложенный тип, способный хранить одно из нескольких «альтернативных» значений, подобно типу union в C. Основное отличие состоит в том, что эти UNION типы являются «мечеными объединениями» и, следовательно, всегда содержат дискриминатор — «метку», которая указывает, какую альтернативу он в данный момент хранит, даже если само внутреннее значение равно NULL. UNION типы, таким образом, более похожи на std::variant в C++17, Enum в Rust или «тип суммы», присутствующий в большинстве функциональных языков.
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 использует функции приведения типов-членов VARCHAR при приведении к 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 |
|---|
| число |
| строка |
| строка |
Приведения типов объединений
По сравнению с другими вложенными типами, UNION позволяют набор неявных приведений для удобного и естественного использования при работе с их членами как «подтипами». Однако эти приведения были разработаны с учетом двух принципов: избежание неоднозначности и избежание приведений, которые могут привести к потере информации. Это предотвращает, чтобы UNION были полностью «прозрачными», но все ещё позволяет UNION типам иметь «супертипное» отношение к их членам.
Таким образом, UNION типы не могут быть неявно приведены к какому-либо типу-члену в общем случае, так как информация в других членах, не соответствующих целевому типу, будет «потеряна». Если вы хотите принудительно привести UNION к одному из его членов, вы должны использовать функцию union_extract явно.
Исключением из этого правила является приведение UNION к VARCHAR, в этом случае члены будут использовать соответствующие VARCHAR приведения. Так как всё может быть приведено к VARCHAR, это «безопасно» в некотором смысле.
Приведение к типам объединений
Тип всегда можно неявно привести к типу объединения, если его можно неявно привести к одному из типов-членов 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 типы являются ковариантными относительно своих членов.
| 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 типов внутри, они могут использоваться со всеми операторами сравнения, а также в предложениях `WHERE` и `ORDER BY` с той же семантикой, что и у STRUCT. «Метка» всегда хранится в качестве первого элемента структуры, что гарантирует, что UNION типы сравниваются и упорядочиваются по «метке» в первую очередь.
Функции
См. Функции объединения.
© Copyright 2018–2024 Stichting DuckDB Foundation
Licensed under the MIT License.
https://duckdb.org/docs/sql/data_types/union.html