Выведение схемы JSON
ClickHouse может автоматически определять структуру данных в формате JSON. Это можно использовать для непосредственного выполнения запросов к JSON-данным, например на локальном диске с помощью clickhouse-local или в S3-бакетах, и/или для автоматического создания схем перед загрузкой данных в ClickHouse.
Когда использовать вывод типов
- Однородная структура — данные, на основе которых вы собираетесь выводить типы, содержат все ключи, которые вас интересуют. Вывод типов основан на выборке данных до максимального числа строк или байт. Данные после выборки, с дополнительными столбцами, будут игнорироваться и будут недоступны для запросов.
- Однородные типы — типы данных для конкретных ключей должны быть совместимы, то есть должна быть возможность автоматически привести один тип к другому.
Если у вас более динамический JSON, в который добавляются новые ключи и для одного и того же пути возможны несколько типов, см. раздел "Работа с полуструктурированными и динамическими данными".
Определение типов
Далее предполагается, что JSON имеет согласованную структуру и один тип для каждого пути.
В наших предыдущих примерах использовалась упрощённая версия набора данных Python PyPI в формате NDJSON. В этом разделе мы рассматриваем более сложный набор данных с вложенными структурами — набор данных arXiv, содержащий 2,5 млн научных статей. Каждая строка этого набора данных, распространяемого в формате NDJSON, представляет собой опубликованную научную работу. Пример строки показан ниже:
Эти данные требуют гораздо более сложной схемы, чем предыдущие примеры. Ниже мы описываем процесс её определения, вводя сложные типы, такие как Tuple и Array.
Этот набор данных хранится в публичном бакете S3 по адресу s3://datasets-documentation/arxiv/arxiv.json.gz.
Как видно, приведённый выше набор данных содержит вложенные объекты JSON. Хотя пользователям следует разрабатывать и версионировать свои схемы, механизм вывода типов позволяет автоматически определять типы по самим данным. Это даёт возможность автоматически генерировать DDL-описание схемы, устраняя необходимость создавать её вручную и ускоряя процесс разработки.
Помимо определения схемы, механизм вывода схемы JSON автоматически определит формат данных по расширению файла и его содержимому. В результате приведённый выше файл автоматически распознаётся как NDJSON.
Использование функции s3 с командой DESCRIBE показывает типы, которые будут автоматически определены.
Вы можете заметить, что многие столбцы определены как Nullable. Мы не рекомендуем использовать тип Nullable, если в этом нет строгой необходимости. Вы можете использовать schema_inference_make_columns_nullable, чтобы управлять тем, в каких случаях применяется Nullable.
Мы видим, что большинство столбцов были автоматически определены как String, при этом столбец update_date корректно определён как Date. Столбец versions был создан как Array(Tuple(created String, version String)) для хранения списка объектов, а authors_parsed определён как Array(Array(String)) для вложенных массивов.
Автоопределение значений типов date и datetime настраивается с помощью параметров input_format_try_infer_dates и input_format_try_infer_datetimes соответственно (оба включены по умолчанию). Интерпретация объектов как кортежей контролируется параметром input_format_json_try_infer_named_tuples_from_objects. Другие параметры, управляющие определением схемы для JSON (например, автоопределением чисел), можно найти здесь.
Запросы к JSON
Далее предполагается, что JSON имеет единообразную структуру и один тип данных для каждого пути.
Мы можем полагаться на вывод схемы, чтобы выполнять запросы непосредственно к JSON-данным. Ниже мы находим топ‑авторов для каждого года, используя то, что даты и массивы автоматически распознаются.
Автоматическое определение схемы позволяет выполнять запросы к JSON-файлам без необходимости явно её задавать, что ускоряет выполнение разовых задач по анализу данных.
Создание таблиц
Мы можем использовать вывод схемы для автоматического создания структуры таблицы. Следующая команда CREATE AS EMPTY заставляет систему вывести DDL для таблицы и создать её. При этом данные не загружаются:
Чтобы проверить структуру таблицы, используем команду SHOW CREATE TABLE:
Выше приведена корректная схема для этих данных. Определение схемы основано на выборочном построчном чтении данных. Значения столбцов извлекаются в соответствии с форматом, а для определения типа каждого значения используются рекурсивные парсеры и эвристики. Максимальное количество строк и байт, читаемых из данных при определении схемы, контролируется настройками input_format_max_rows_to_read_for_schema_inference (по умолчанию 25000) и input_format_max_bytes_to_read_for_schema_inference (по умолчанию 32 МБ). Если определение окажется некорректным, пользователи могут задать подсказки, как описано здесь.
Создание таблиц из фрагментов
В приведённом выше примере используется файл на S3 для создания схемы таблицы. При необходимости можно создать схему из однострочного фрагмента данных. Это можно сделать с помощью функции format, как показано ниже:
Загрузка JSON-данных
Далее предполагается, что JSON имеет единообразную структуру и содержит один тип значений для каждого пути.
Предыдущие команды создали таблицу, в которую можно загружать данные. Теперь вы можете вставить данные в таблицу, используя следующую команду INSERT INTO SELECT:
Примеры загрузки данных из других источников, например из файла, см. здесь.
После загрузки можно выполнять запросы к данным, при желании используя формат PrettyJSONEachRow, чтобы отображать строки в их исходной структуре:
Обработка ошибок
Иногда во входных данных могут встречаться ошибки. Например, отдельные столбцы могут иметь неверный тип или JSON-объект может быть некорректно отформатирован. Для таких случаев вы можете использовать настройки input_format_allow_errors_num и input_format_allow_errors_ratio, чтобы разрешить игнорирование определённого числа строк, если данные вызывают ошибки операции вставки. Дополнительно можно задать подсказки, чтобы упростить вывод схемы.
Работа с полуструктурированными и динамическими данными
В нашем предыдущем примере использовался JSON с фиксированной схемой, с хорошо известными именами ключей и типами. На практике это часто не так — ключи могут добавляться, а их типы меняться. Это типично, например, для данных для наблюдаемости (Observability).
ClickHouse обрабатывает такие случаи с помощью специализированного типа JSON.
Если вы знаете, что ваш JSON очень динамичен, содержит множество уникальных ключей и несколько типов для одних и тех же ключей, мы не рекомендуем использовать вывод схемы с помощью JSONEachRow, пытаясь вывести отдельный столбец для каждого ключа — даже если данные находятся в формате JSON с разделением по строкам (newline-delimited JSON).
Рассмотрим следующий пример из расширенной версии указанного выше набора данных Python PyPI dataset. Здесь мы добавили произвольный столбец tags со случайными парами ключ–значение.
Образец этих данных общедоступен в формате JSON, где каждая запись находится на отдельной строке. Если попытаться автоматически вывести схему для этого файла, вы обнаружите, что производительность окажется низкой, а ответ — крайне объёмным:
Основная проблема здесь заключается в том, что для вывода типов используется формат JSONEachRow. Он пытается вывести тип столбца для каждого ключа в JSON — по сути, пытаясь применить статическую схему к данным без использования типа JSON.
При наличии тысяч уникальных столбцов такой подход к выводу типов работает медленно. В качестве альтернативы пользователи могут использовать формат JSONAsObject.
JSONAsObject рассматривает весь входной JSON как один объект и сохраняет его в одном столбце типа JSON, что делает его более подходящим для высокодинамичных или вложенных JSON-нагрузок.
Этот формат также необходим в случаях, когда столбцы имеют несколько типов данных, которые невозможно привести к общему виду. Например, рассмотрим файл sample.json со следующим JSON, в котором каждая запись находится на отдельной строке:
В этом случае ClickHouse может устранить конфликт типов и интерпретировать столбец a как Nullable(String).
Это приведение типов можно контролировать с помощью ряда настроек. Приведённый выше пример зависит от настройки input_format_json_read_numbers_as_strings.
Однако некоторые типы несовместимы. Рассмотрим следующий пример:
В этом случае невозможно выполнить какое-либо приведение типов. Команда DESCRIBE завершится с ошибкой:
Получено исключение от сервера (версия 24.12.1): Код: 636. DB::Exception: Получено от sql-clickhouse.clickhouse.com:9440. DB::Exception: Не удаётся извлечь структуру таблицы из файла в формате JSON. Ошибка: Код: 53. DB::Exception: Тип Tuple(b Int64), автоматически определённый для столбца 'a' в строке 1, отличается от типа, определённого в предыдущих строках: Int64. Вы можете явно указать тип для этого столбца с помощью настройки schema_inference_hints.
Дополнительные материалы
Подробнее о выводе типов данных см. на этой странице документации.