Learning Platform
Глоссарий Troubleshooting
Урок 05.06 · 22 мин
Средний
type-systemvariantgeometryduckdb-15

VARIANT и GEOMETRY: новые типы DuckDB 1.5

Версия DuckDB 1.5 (релиз «Variegata», март 2026) добавила в систему типов два важных пополнения. VARIANT — тип для полуструктурированных данных, чья форма заранее неизвестна. GEOMETRY — тип для пространственных данных, и важно, что в 1.5 он стал core-типом: он встроен в ядро, тогда как раньше был доступен только через расширение spatial.

Оба типа закрывают пробелы, которые до 1.5 приходилось обходить. VARIANT решает то, с чем не справляются ни STRUCT, ни MAP, ни даже JSON. GEOMETRY делает пространственную аналитику доступной без явной установки расширения. Этот урок — про обе новинки: что они дают, как устроены и когда применять. Курс ориентируется на актуальные DuckDB 1.5.2 stable и LTS-линию 1.4.x «Andium», и VARIANT с GEOMETRY — часть этой актуальной картины.


Проблема, которую решает VARIANT

В уроках про STRUCT, LIST и MAP мы видели: каждый из них требует какой-то предсказуемости. STRUCT требует фиксированную схему. MAP требует единый тип ключей и единый тип значений. Но бывают данные, у которых нет ни того, ни другого: вложенный объект, где у одного события поле payload — это число, у другого — строка, у третьего — вложенный массив объектов. Структура открыта и непредсказуема.

До 1.5 такие данные хранили в типе JSON. JSON гибок, но у него два недостатка.

JSONB глубже: containment, jsonb_path, изменение

ClickHouse: JSON type, Variant и Dynamic типы Первый — JSON в DuckDB по сути хранится как текст: каждый раз при доступе к полю строку нужно разбирать (парсить). Второй — система типов JSON беднее, чем у DuckDB: в JSON нет настоящих DATE, TIMESTAMP, DECIMAL — только строки, числа, булевы, объекты, массивы. Дата в JSON — это строка, и её типизированность теряется.

VARIANT — новый тип, спроектированный именно для полуструктурированных данных. Он хранит данные в типизированном бинарном формате, а не как текст. И он поддерживает больше типов, чем JSON, — в частности, темпоральные типы вроде DATE и TIMESTAMP сохраняются как настоящие типы, а не как строки.

VARIANT против JSON для полуструктурированных данных
JSONХранится по сути как текст. Доступ к полю требует разбора строки. Типы ограничены: нет настоящих DATE, TIMESTAMP, DECIMAL.
1.5 добавляет
VARIANTХранит данные в типизированном бинарном формате. Поддерживает больше типов, чем JSON, включая темпоральные. Не требует парсинга текста при доступе.

Как работать с VARIANT

VARIANT принимает значение почти любой формы и сохраняет его вместе с информацией о типе. Узнать, какой тип лежит внутри конкретного VARIANT-значения, можно функцией variant_typeof, а достать вложенное поле — функцией variant_extract:

-- Привести значение к VARIANT
SELECT
    {'user': 'anna', 'age': 30, 'tags': ['a', 'b']}::VARIANT AS v;

-- Узнать тип, лежащий внутри VARIANT-значения
SELECT variant_typeof( 42::VARIANT )            AS t_num,
       variant_typeof( 'hello'::VARIANT )       AS t_str,
       variant_typeof( [1,2,3]::VARIANT )       AS t_list;

-- Достать вложенное поле из VARIANT
SELECT variant_extract(v, 'user') AS user_field
FROM events;

Принципиальное отличие VARIANT от STRUCT: у STRUCT тип фиксирован и известен компилятору запроса, а у VARIANT — нет. Каждое VARIANT-значение само несёт описание своего типа, и узнать форму можно только во время выполнения, через variant_typeof. Это та же идея, что у UNION с его тегом, но UNION ограничен заранее перечисленным набором вариантов, а VARIANT открыт — он принимает произвольную форму.

Ещё одно практически важное свойство VARIANT — поддержка Parquet shredding. «Shredding» — это разбор полуструктурированного значения на отдельные типизированные колонки при записи в Parquet. Колонка VARIANT, у которой часть полей встречается стабильно, может быть «расшита» в обычные колонки Parquet, и тогда чтение этих полей становится колоночным и быстрым. Это снимает главный минус хранения полуструктурированных данных — потерю колоночности.

NOTE

Иерархия выбора типа для данных с переменной формой такая. Структура фиксирована и известна заранее — STRUCT. Структура — это словарь с едиными типами ключей и значений — MAP. Структура полностью произвольна и непредсказуема — VARIANT. JSON остаётся для совместимости и случаев, когда нужен именно текстовый JSON на вводе-выводе, но для хранения и обработки полуструктурированных данных внутри DuckDB 1.5 VARIANT — более типизированный и эффективный выбор.


GEOMETRY: пространственный тип в ядре

GEOMETRY — тип для пространственных данных: точек, линий, полигонов на плоскости. Точка — координата объекта, линия — маршрут или граница, полигон — область, регион, контур здания. Пространственная аналитика отвечает на вопросы «какие объекты внутри этой области», «какое расстояние между точками», «пересекаются ли эти границы».

Главная новость 1.5 — GEOMETRY стал core-типом. До 1.5 пространственные возможности жили в расширении spatial, которое нужно было явно установить и загрузить (INSTALL spatial; LOAD spatial;) — и, в отличие от многих расширений, spatial не подгружается автоматически. В 1.5 сам тип GEOMETRY встроен в ядро DuckDB. Это значит, что хранить геометрию, читать её, держать в колонке таблицы можно без расширения. Расширение spatial по-прежнему нужно для богатого набора пространственных функций и форматов, но базовый тип теперь часть ядра.

-- GEOMETRY-колонка и литералы из WKT-текста
CREATE TABLE places (
    id INTEGER,
    name VARCHAR,
    location GEOMETRY
);

INSERT INTO places VALUES
    (1, 'Москва', ST_Point(37.6173, 55.7558)),
    (2, 'Казань', ST_Point(49.1221, 55.7887));

Как хранится GEOMETRY

GEOMETRY внутри хранится в формате WKB (Well-Known Binary) — это стандартное компактное бинарное представление геометрических объектов, принятое в геоинформатике. DuckDB использует little-endian вариант WKB. WKB кодирует тип фигуры (точка, линия, полигон) и её координаты в виде байтов.

У GEOMETRY-колонки в DuckDB есть две оптимизации, которые стоит знать.

Первая — статистика ограничивающих прямоугольников (bounding box) на уровне row group. Для каждой группы строк DuckDB хранит прямоугольник, охватывающий все геометрии группы. Когда запрос фильтрует по области («объекты в этом прямоугольнике»), движок по bounding-box-статистике сразу пропускает группы, чьи прямоугольники не пересекаются с областью запроса. Это пространственный аналог zonemap — статистики min/max, по которой DuckDB пропускает блоки данных при обычной фильтрации.

Вторая — shredding для сжатия. Если колонка содержит однородные геометрии (например, все значения — точки), DuckDB может «расшить» их в более плотное представление. Для однородных геометрических колонок shredding даёт примерно трёхкратное сжатие.

GEOMETRY: WKB-хранение и пропуск по bounding box
GEOMETRY хранится как WKBWell-Known Binary — стандартное компактное бинарное представление фигур. DuckDB использует little-endian вариант.
на каждый row group
bounding box группыДля группы строк хранится охватывающий прямоугольник всех её геометрий — пространственный аналог zonemap.
фильтр по области
пропуск непересекающихся группЕсли прямоугольник группы не пересекается с областью запроса, всю группу можно пропустить, не читая.

GEOMETRY поддерживает опциональную привязку к системе координат (CRS, coordinate reference system) — указание, в какой системе заданы координаты (географические широта/долгота, проекционная система в метрах и т. д.). CRS важен для корректности пространственных расчётов: расстояние между точками считается по-разному в зависимости от системы координат.

TIP

Для реальной пространственной аналитики — вычисления расстояний, проверки пересечений, буферов, пространственных join — нужны функции из расширения spatial (ST_Distance, ST_Intersects, ST_Within и десятки других). То, что GEOMETRY стал core-типом, означает только, что сам тип и его хранение встроены в ядро; богатую пространственную алгебру по-прежнему даёт расширение. Установите spatial, когда задача выходит за рамки просто хранения геометрии.


Две новинки в общей картине типов

VARIANT и GEOMETRY расширяют систему типов DuckDB в двух разных направлениях, и оба — про то, чтобы не выходить за пределы DuckDB ради специализированной задачи.

VARIANT закрывает направление «данные без фиксированной формы»: раньше для них был только текстовый JSON с бедной типизацией, теперь есть типизированный бинарный тип, понимающий темпоральные типы и поддерживающий Parquet shredding.

GEOMETRY закрывает направление «пространственные данные»: раньше пространственность требовала явной установки расширения даже для хранения, теперь базовый тип — часть ядра, с bounding-box-статистикой для пропуска групп и shredding для сжатия.

ТипНаправлениеЧто было до 1.5Что даёт 1.5
VARIANTПолуструктурированные данныеТекстовый JSON, бедная типизацияТипизированный бинарный формат, темпоральные типы, shredding
GEOMETRYПространственные данныеТолько через расширение spatialCore-тип в ядре, WKB-хранение, bounding-box-статистика

Оба типа подтверждают общую линию DuckDB: быть полноценной аналитической СУБД, в которой специализированные задачи — полуструктурированные и геоданные — решаются нативно, без обходных путей и без вынужденного экспорта данных в другой инструмент.


Попробуй сам

Запустите DuckDB CLI (версия 1.5 или новее):

-- VARIANT
SELECT variant_typeof( 42::VARIANT ) AS t1,
       variant_typeof( 'text'::VARIANT ) AS t2,
       variant_typeof( DATE '2026-01-01'::VARIANT ) AS t3;

Задания:

  1. Выполните запрос выше. Обратите внимание, как variant_typeof различает число, строку и дату — попробуйте мысленно сделать то же с типом JSON и объясните, почему дата там была бы строкой.
  2. Создайте VARIANT из вложенного объекта ({'a': 1, 'b': [2, 3]}::VARIANT) и достаньте поле a через variant_extract.
  3. Объясните своими словами разницу между STRUCT и VARIANT с точки зрения того, когда становится известна форма данных.
  4. Создайте таблицу с GEOMETRY-колонкой и вставьте несколько точек через ST_Point. Убедитесь, что для этого не понадобилось устанавливать расширение spatial.
  5. Объясните, как bounding-box-статистика на уровне row group ускоряет запрос «найди объекты в заданной прямоугольной области», и в чём её аналогия с обычным zonemap.

Проверка знанийKnowledge check
Какую задачу решает тип VARIANT сверх JSON, и что означает, что GEOMETRY в DuckDB 1.5 стал core-типом?
ОтветAnswer
VARIANT — тип для полуструктурированных данных, чья форма заранее неизвестна и непредсказуема: вложенный объект, где одно и то же поле в разных строках то число, то строка, то массив. До DuckDB 1.5 такие данные хранили в типе JSON, у которого два недостатка. Первый: JSON в DuckDB по сути хранится как текст, и доступ к полю требует каждый раз разбирать (парсить) строку. Второй: система типов JSON беднее, чем у DuckDB — нет настоящих DATE, TIMESTAMP, DECIMAL, дата в JSON это строка, и типизированность теряется. VARIANT хранит данные в типизированном бинарном формате, а не как текст, и поддерживает больше типов, чем JSON, в частности темпоральные DATE и TIMESTAMP сохраняются как настоящие типы. Каждое VARIANT-значение само несёт описание своего типа, и узнать форму можно во время выполнения через variant_typeof — этим VARIANT отличается от STRUCT, чья схема фиксирована и известна компилятору запроса заранее. VARIANT также поддерживает Parquet shredding — разбор стабильных полей на колонки Parquet, что возвращает колоночность. GEOMETRY — тип для пространственных данных (точки, линии, полигоны). То, что в 1.5 он стал core-типом, означает: сам тип GEOMETRY и его хранение встроены в ядро DuckDB, и держать геометрию в колонке можно без расширения, тогда как раньше пространственность требовала явной установки и загрузки расширения spatial (которое к тому же не подгружается автоматически). GEOMETRY хранится в формате WKB (Well-Known Binary, little-endian), для каждой row group хранится bounding box для пропуска непересекающихся групп при фильтре по области (пространственный аналог zonemap), а для однородных геометрий shredding даёт примерно трёхкратное сжатие. При этом богатая пространственная алгебра — расчёт расстояний, пересечений — по-прежнему требует расширения spatial; core-типом стал именно базовый тип и его хранение.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Какое преимущество даёт тип VARIANT по сравнению с типом JSON для хранения полуструктурированных данных?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 6