> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-fix-nav-issues.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Используйте JSON там, где это уместно

> Страница о том, когда использовать JSON

ClickHouse теперь поддерживает нативный тип столбца JSON, предназначенный для полуструктурированных и динамических данных. Важно уточнить, что **это тип столбца, а не формат данных** — вы можете вставлять JSON в ClickHouse как строку или через поддерживаемые форматы, например [JSONEachRow](/ru/reference/formats/JSON/JSONEachRow), но это не означает, что вы используете тип столбца JSON. Тип JSON следует использовать только тогда, когда структура ваших данных динамична, а не просто в тех случаях, когда вы храните JSON.

<div id="when-to-use-json-type">
  ## Когда использовать тип `JSON`
</div>

Тип `JSON` предназначен для запросов, фильтрации и агрегации по отдельным полям в объектах JSON с динамической или непредсказуемой структурой. Для этого объекты JSON разбиваются на отдельные подстолбцы, что значительно уменьшает объём читаемых данных и ускоряет запросы по выбранным полям по сравнению с такими альтернативами, как `Map` или разбор строк.

**Однако у этого подхода есть важные недостатки:**

* Более медленные `INSERT` - Разбиение JSON на подстолбцы, определение типов и управление гибкими структурами хранения делают вставку медленнее по сравнению с хранением JSON в виде простого столбца `String`.
* Медленнее при чтении объектов целиком - Если вам нужно получать JSON-документы целиком, а не отдельные поля, тип `JSON` работает медленнее, чем чтение из столбца `String`. Дополнительные затраты на восстановление объектов из отдельных подстолбцов не дают преимуществ, если вы не выполняете запрос по отдельным полям.
* Дополнительные накладные расходы на хранение - Поддержка отдельных подстолбцов создаёт дополнительные структурные накладные расходы по сравнению с хранением JSON как одного строкового значения.

<div id="use-json-type">
  ### Используйте тип `JSON`, когда:
</div>

* У ваших данных динамическая или непредсказуемая структура, а ключи различаются от документа к документу
* Типы полей или схемы меняются со временем либо различаются между записями
* Вам нужно выполнять запросы, фильтровать или агрегировать данные по определённым путям внутри объектов JSON, структуру которых невозможно заранее предсказать
* Ваш сценарий предполагает работу с полуструктурированными данными, такими как журнал, события или пользовательский контент с непоследовательными схемами

<div id="use-string-type">
  ### Используйте столбец `String` (или структурированные типы), когда:
</div>

* Структура ваших данных известна и стабильна — в этом случае лучше использовать обычные столбцы, типы `Tuple`, `Array`, `Dynamic` или `Variant`
* Документы `JSON` рассматриваются как непрозрачные blob-объекты, которые только хранятся и извлекаются целиком, без анализа на уровне полей
* Вам не нужно выполнять запросы или фильтровать данные по отдельным полям JSON в базе данных
* `JSON` — это просто формат передачи/хранения, а не формат, который анализируется в ClickHouse

<Tip>
  Если `JSON` — это непрозрачный документ, который не анализируется в базе данных, а только сохраняется и затем извлекается целиком, его следует хранить в поле `String`. Преимущества типа `JSON` проявляются только тогда, когда вам нужно эффективно выполнять запросы, фильтрацию или агрегацию по конкретным полям в динамических структурах `JSON`.

  Вы также можете комбинировать подходы — использовать стандартные столбцы для предсказуемых полей верхнего уровня и столбец `JSON` для динамических частей полезной нагрузки.
</Tip>

<div id="considerations-and-tips-for-using-json">
  ## Рекомендации и советы по использованию JSON
</div>

Тип JSON обеспечивает эффективное столбцовое хранение, разворачивая пути в подстолбцы. Но гибкость требует ответственного подхода. Чтобы использовать его эффективно:

* **Указывайте типы путей** с помощью [подсказок в определении столбца](/ru/reference/data-types/newjson), чтобы задавать типы для известных подстолбцов и избегать лишнего вывода типов.
* **Пропускайте пути**, если эти значения вам не нужны, с помощью [SKIP и SKIP REGEXP](/ru/reference/data-types/newjson), чтобы сократить объём хранения и повысить производительность.
* **Не задавайте [`max_dynamic_paths`](/ru/reference/data-types/newjson#reaching-the-limit-of-dynamic-paths-inside-json) слишком большим** — большие значения увеличивают потребление ресурсов и снижают эффективность. Как правило, держите его ниже 10 000.

<Info>
  **Подсказки типов**

  Подсказки типов — это не просто способ избежать лишнего вывода типов: они полностью устраняют дополнительный уровень косвенности при хранении и обработке. Пути JSON с подсказками типов всегда хранятся так же, как обычные столбцы, без необходимости использовать [**столбцы-дискриминаторы**](https://clickhouse.com/blog/a-new-powerful-json-data-type-for-clickhouse#storage-extension-for-dynamically-changing-data) или выполнять динамическое разрешение во время выполнения запроса. Это означает, что при хорошо заданных подсказках типов вложенные поля JSON обеспечивают ту же производительность и эффективность, как если бы они изначально были смоделированы как поля верхнего уровня. В результате для датасетов, которые в целом однородны, но при этом выигрывают от гибкости JSON, подсказки типов позволяют сохранить производительность без необходимости перестраивать схему или конвейер приёма.
</Info>

<div id="advanced-features">
  ## Расширенные возможности
</div>

* JSON-столбцы **можно использовать в первичных ключах** так же, как и любые другие столбцы. Для подстолбцов нельзя указывать кодеки.
* Они поддерживают интроспекцию с помощью таких функций, как [`JSONAllPathsWithTypes()` и `JSONDynamicPaths()`](/ru/reference/data-types/newjson#introspection-functions).
* Вы можете читать вложенные объекты с помощью синтаксиса `.^`.
* Синтаксис запросов может отличаться от стандартного SQL и требовать специального приведения типов или операторов для вложенных полей.

Дополнительные рекомендации см. в[ документации ClickHouse по JSON](/ru/reference/data-types/newjson) или в нашей статье блога[ A New Powerful JSON Data Type for ClickHouse](https://clickhouse.com/blog/a-new-powerful-json-data-type-for-clickhouse).

<div id="examples">
  ## Примеры
</div>

Рассмотрим следующий образец JSON, представляющий строку из [набора данных Python PyPI](https://clickpy.clickhouse.com/):

```json theme={null}
{
  "date": "2022-11-15",
  "country_code": "ES",
  "project": "clickhouse-connect",
  "type": "bdist_wheel",
  "installer": "pip",
  "python_minor": "3.9",
  "system": "Linux",
  "version": "0.3.0"
}
```

Предположим, что эта схема статична и типы можно чётко определить. Даже если данные находятся в формате NDJSON (одна JSON-строка на строку), для такой схемы нет необходимости использовать тип JSON. Просто определите схему с помощью классических типов.

```sql theme={null}
CREATE TABLE pypi (
  `date` Date,
  `country_code` String,
  `project` String,
  `type` String,
  `installer` String,
  `python_minor` String,
  `system` String,
  `version` String
)
ENGINE = MergeTree
ORDER BY (project, date)
```

и вставьте JSON-строки:

```sql theme={null}
INSERT INTO pypi FORMAT JSONEachRow
{"date":"2022-11-15","country_code":"ES","project":"clickhouse-connect","type":"bdist_wheel","installer":"pip","python_minor":"3.9","system":"Linux","version":"0.3.0"}
```

Рассмотрим [датасет arXiv](https://www.kaggle.com/datasets/Cornell-University/arxiv?resource=download), содержащий 2,5 млн научных публикаций. Каждая строка в этом датасете, распространяемом в формате NDJSON, соответствует одной опубликованной научной работе. Пример строки показан ниже:

```json theme={null}
{
  "id": "2101.11408",
  "submitter": "Daniel Lemire",
  "authors": "Daniel Lemire",
  "title": "Number Parsing at a Gigabyte per Second",
  "comments": "Software at https://github.com/fastfloat/fast_float and\n  https://github.com/lemire/simple_fastfloat_benchmark/",
  "journal-ref": "Software: Practice and Experience 51 (8), 2021",
  "doi": "10.1002/spe.2984",
  "report-no": null,
  "categories": "cs.DS cs.MS",
  "license": "http://creativecommons.org/licenses/by/4.0/",
  "abstract": "With disks and networks providing gigabytes per second ....\n",
  "versions": [
    {
      "created": "Mon, 11 Jan 2021 20:31:27 GMT",
      "version": "v1"
    },
    {
      "created": "Sat, 30 Jan 2021 23:57:29 GMT",
      "version": "v2"
    }
  ],
  "update_date": "2022-11-07",
  "authors_parsed": [
    [
      "Lemire",
      "Daniel",
      ""
    ]
  ]
}
```

Хотя JSON здесь сложный и содержит вложенные структуры, его формат предсказуем. Количество и типы полей не меняются. Хотя в этом примере можно было бы использовать тип JSON, структуру также можно явно определить с помощью типов [Tuple](/ru/reference/data-types/tuple) и [Nested](/ru/reference/data-types/nested-data-structures):

```sql theme={null}
CREATE TABLE arxiv
(
  `id` String,
  `submitter` String,
  `authors` String,
  `title` String,
  `comments` String,
  `journal-ref` String,
  `doi` String,
  `report-no` String,
  `categories` String,
  `license` String,
  `abstract` String,
  `versions` Array(Tuple(created String, version String)),
  `update_date` Date,
  `authors_parsed` Array(Array(String))
)
ENGINE = MergeTree
ORDER BY update_date
```

Снова можно вставить данные в формате JSON:

```sql theme={null}
INSERT INTO arxiv FORMAT JSONEachRow 
{"id":"2101.11408","submitter":"Daniel Lemire","authors":"Daniel Lemire","title":"Number Parsing at a Gigabyte per Second","comments":"Software at https://github.com/fastfloat/fast_float and\n  https://github.com/lemire/simple_fastfloat_benchmark/","journal-ref":"Software: Practice and Experience 51 (8), 2021","doi":"10.1002/spe.2984","report-no":null,"categories":"cs.DS cs.MS","license":"http://creativecommons.org/licenses/by/4.0/","abstract":"With disks and networks providing gigabytes per second ....\n","versions":[{"created":"Mon, 11 Jan 2021 20:31:27 GMT","version":"v1"},{"created":"Sat, 30 Jan 2021 23:57:29 GMT","version":"v2"}],"update_date":"2022-11-07","authors_parsed":[["Lemire","Daniel",""]]}
```

Предположим, добавлен ещё один столбец с именем `tags`. Если бы это был просто список строк, мы могли бы представить его как `Array(String)`, но давайте предположим, что можно добавлять произвольные структуры тегов со смешанными типами (обратите внимание, `score` может быть строкой или целым числом). Наш изменённый JSON-документ:

```sql theme={null}
{
 "id": "2101.11408",
 "submitter": "Daniel Lemire",
 "authors": "Daniel Lemire",
 "title": "Number Parsing at a Gigabyte per Second",
 "comments": "Software at https://github.com/fastfloat/fast_float and\n  https://github.com/lemire/simple_fastfloat_benchmark/",
 "journal-ref": "Software: Practice and Experience 51 (8), 2021",
 "doi": "10.1002/spe.2984",
 "report-no": null,
 "categories": "cs.DS cs.MS",
 "license": "http://creativecommons.org/licenses/by/4.0/",
 "abstract": "With disks and networks providing gigabytes per second ....\n",
 "versions": [
 {
   "created": "Mon, 11 Jan 2021 20:31:27 GMT",
   "version": "v1"
 },
 {
   "created": "Sat, 30 Jan 2021 23:57:29 GMT",
   "version": "v2"
 }
 ],
 "update_date": "2022-11-07",
 "authors_parsed": [
 [
   "Lemire",
   "Daniel",
   ""
 ]
 ],
 "tags": {
   "tag_1": {
     "name": "ClickHouse user",
     "score": "A+",
     "comment": "A good read, applicable to ClickHouse"
   },
   "28_03_2025": {
     "name": "professor X",
     "score": 10,
     "comment": "Didn't learn much",
     "updates": [
       {
         "name": "professor X",
         "comment": "Wolverine found more interesting"
       }
     ]
   }
 }
}
```

В этом случае документы arXiv можно было бы смоделировать либо целиком в формате JSON, либо просто добавить столбец `tags` типа JSON. Ниже приведены оба примера:

```sql theme={null}
CREATE TABLE arxiv
(
  `doc` JSON(update_date Date)
)
ENGINE = MergeTree
ORDER BY doc.update_date
```

<Note>
  Мы указываем подсказку типа для столбца `update_date` в определении JSON, так как используем его в ключе сортировки/первичном ключе. Это помогает ClickHouse понять, что этот столбец не может быть null, и определить, какой подстолбец `update_date` использовать (для каждого типа их может быть несколько, поэтому иначе возникает неоднозначность).
</Note>

Мы можем выполнить вставку в эту таблицу и просмотреть затем автоматически выведенную схему с помощью функции [`JSONAllPathsWithTypes`](/ru/reference/functions/regular-functions/json-functions#JSONAllPathsWithTypes) и output format [`PrettyJSONEachRow`](/ru/reference/formats/JSON/PrettyJSONEachRow):

```sql theme={null}
INSERT INTO arxiv FORMAT JSONAsObject 
{"id":"2101.11408","submitter":"Daniel Lemire","authors":"Daniel Lemire","title":"Number Parsing at a Gigabyte per Second","comments":"Software at https://github.com/fastfloat/fast_float and\n  https://github.com/lemire/simple_fastfloat_benchmark/","journal-ref":"Software: Practice and Experience 51 (8), 2021","doi":"10.1002/spe.2984","report-no":null,"categories":"cs.DS cs.MS","license":"http://creativecommons.org/licenses/by/4.0/","abstract":"With disks and networks providing gigabytes per second ....\n","versions":[{"created":"Mon, 11 Jan 2021 20:31:27 GMT","version":"v1"},{"created":"Sat, 30 Jan 2021 23:57:29 GMT","version":"v2"}],"update_date":"2022-11-07","authors_parsed":[["Lemire","Daniel",""]],"tags":{"tag_1":{"name":"ClickHouse user","score":"A+","comment":"A good read, applicable to ClickHouse"},"28_03_2025":{"name":"professor X","score":10,"comment":"Didn't learn much","updates":[{"name":"professor X","comment":"Wolverine found more interesting"}]}}}
```

```sql theme={null}
SELECT JSONAllPathsWithTypes(doc)
FROM arxiv
FORMAT PrettyJSONEachRow

{
  "JSONAllPathsWithTypes(doc)": {
    "abstract": "String",
    "authors": "String",
    "authors_parsed": "Array(Array(Nullable(String)))",
    "categories": "String",
    "comments": "String",
    "doi": "String",
    "id": "String",
    "journal-ref": "String",
    "license": "String",
    "submitter": "String",
    "tags.28_03_2025.comment": "String",
    "tags.28_03_2025.name": "String",
    "tags.28_03_2025.score": "Int64",
    "tags.28_03_2025.updates": "Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))",
    "tags.tag_1.comment": "String",
    "tags.tag_1.name": "String",
    "tags.tag_1.score": "String",
    "title": "String",
    "update_date": "Date",
    "versions": "Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))"
  }
}

1 row in set. Elapsed: 0.003 sec.
```

В качестве альтернативы это можно смоделировать, используя описанную ранее схему и JSON-столбец `tags`. Обычно этот вариант предпочтителен, поскольку сводит к минимуму объём автоматически определяемых ClickHouse данных:

```sql theme={null}
CREATE TABLE arxiv
(
    `id` String,
    `submitter` String,
    `authors` String,
    `title` String,
    `comments` String,
    `journal-ref` String,
    `doi` String,
    `report-no` String,
    `categories` String,
    `license` String,
    `abstract` String,
    `versions` Array(Tuple(created String, version String)),
    `update_date` Date,
    `authors_parsed` Array(Array(String)),
    `tags` JSON()
)
ENGINE = MergeTree
ORDER BY update_date
```

```sql theme={null}
INSERT INTO arxiv FORMAT JSONEachRow 
{"id":"2101.11408","submitter":"Daniel Lemire","authors":"Daniel Lemire","title":"Number Parsing at a Gigabyte per Second","comments":"Software at https://github.com/fastfloat/fast_float and\n  https://github.com/lemire/simple_fastfloat_benchmark/","journal-ref":"Software: Practice and Experience 51 (8), 2021","doi":"10.1002/spe.2984","report-no":null,"categories":"cs.DS cs.MS","license":"http://creativecommons.org/licenses/by/4.0/","abstract":"With disks and networks providing gigabytes per second ....\n","versions":[{"created":"Mon, 11 Jan 2021 20:31:27 GMT","version":"v1"},{"created":"Sat, 30 Jan 2021 23:57:29 GMT","version":"v2"}],"update_date":"2022-11-07","authors_parsed":[["Lemire","Daniel",""]],"tags":{"tag_1":{"name":"ClickHouse user","score":"A+","comment":"A good read, applicable to ClickHouse"},"28_03_2025":{"name":"professor X","score":10,"comment":"Didn't learn much","updates":[{"name":"professor X","comment":"Wolverine found more interesting"}]}}}
```

Теперь мы можем вывести типы подстолбца `tags`.

```sql theme={null}
SELECT JSONAllPathsWithTypes(tags)
FROM arxiv
FORMAT PrettyJSONEachRow

{
  "JSONAllPathsWithTypes(tags)": {
    "28_03_2025.comment": "String",
    "28_03_2025.name": "String",
    "28_03_2025.score": "Int64",
    "28_03_2025.updates": "Array(JSON(max_dynamic_types=16, max_dynamic_paths=256))",
    "tag_1.comment": "String",
    "tag_1.name": "String",
    "tag_1.score": "String"
  }
}
```

```response theme={null}
1 row in set. Elapsed: 0.002 sec.
```
