clickhouse-local or S3 buckets, and/or automatically create schemas prior to loading the data into ClickHouse.
When to use type inference
- Consistent structure - The data from which you’re going to infer types contains all the keys that you’re interested in. Type inference is based on sampling the data up to a maximum number of rows or bytes. Data after the sample, with additional columns, will be ignored and can’t be queried.
- Consistent types - Data types for specific keys need to be compatible i.e. it must be possible to coerce one type to the other automatically.
Detecting types
The following assumes the JSON is consistently structured and has a single type for each path. Our previous examples used a simple version of the Python PyPI dataset inNDJSON format. In this section, we explore a more complex dataset with nested structures - the arXiv dataset containing 2.5m scholarly papers. Each row in this dataset, distributed as NDJSON, represents a published academic paper. An example row is shown below:
Tuple and Array.
This dataset is stored in a public S3 bucket at s3://datasets-documentation/arxiv/arxiv.json.gz.
You can see that the dataset above contains nested JSON objects. While you should draft and version your schemas, inference allows types to be inferred from the data. This allows the schema DDL to be auto-generated, avoiding the need to build it manually and accelerating the development process.
Auto format detectionAs well as detecting the schema, JSON schema inference will automatically infer the format of the data from the file extension and contents. The above file is detected as being NDJSON automatically as a result.
DESCRIBE command shows the types that will be inferred.
Avoid nullsYou can see a lot of the columns are detected as Nullable. We don’t recommend using the Nullable type when not absolutely needed. You can use schema_inference_make_columns_nullable to control the behavior of when Nullable is applied.
String, with update_date column correctly detected as a Date. The versions column has been created as an Array(Tuple(created String, version String)) to store a list of objects, with authors_parsed being defined as Array(Array(String)) for nested arrays.
Controlling type detectionThe auto-detection of dates and datetimes can be controlled through the settings
input_format_try_infer_dates and input_format_try_infer_datetimes respectively (both enabled by default). The inference of objects as tuples is controlled by the setting input_format_json_try_infer_named_tuples_from_objects. Other settings which control schema inference for JSON, such as the auto-detection of numbers, can be found here.Querying JSON
The following assumes the JSON is consistently structured and has a single type for each path. We can rely on schema inference to query JSON data in place. Below, we find the top authors for each year, exploiting the fact the dates and arrays are automatically detected.Creating tables
We can rely on schema inference to create the schema for a table. The followingCREATE AS EMPTY command causes the DDL for the table to be inferred and the table to created. This doesn’t load any data:
SHOW CREATE TABLE command:
input_format_max_rows_to_read_for_schema_inference (25000 by default) and input_format_max_bytes_to_read_for_schema_inference (32MB by default). In the event detection isn’t correct, you can provide hints as described here.
Creating tables from snippets
The above example uses a file on S3 to create the table schema. You may wish to create a schema from a single-row snippet. This can be achieved using the format function as shown below:Loading JSON data
The following assumes the JSON is consistently structured and has a single type for each path. The previous commands created a table to which data can be loaded. You can now insert the data into your table using the followingINSERT INTO SELECT:
PrettyJSONEachRow to show the rows in their original structure:
Handling errors
Sometimes, you might have bad data. For example, specific columns that don’t have the right type or an improperly formatted JSON object. For this, you can use the settingsinput_format_allow_errors_num and input_format_allow_errors_ratio to allow a certain number of rows to be ignored if the data is triggering insert errors. Additionally, hints can be provided to assist inference.
Working with semi-structured and dynamic data
Our previous example used JSON which was static with well known key names and types. This is often not the case - keys can be added or their types can change. This is common in use cases such as Observability data. ClickHouse handles this through a dedicatedJSON type.
If you know your JSON is highly dynamic with many unique keys and multiple types for the same keys, we recommend not using schema inference with JSONEachRow to try and infer a column for each key - even if the data is in newline-delimited JSON format.
Consider the following example from an extended version of the above Python PyPI dataset dataset. Here we have added an arbitrary tags column with random key value pairs.
JSONEachRow format is used for inference. This attempts to infer a column type per key in the JSON - effectively trying to apply a static schema to the data without using the JSON type.
With thousands of unique columns this approach to inference is slow. As an alternative, you can use the JSONAsObject format.
JSONAsObject treats the entire input as a single JSON object and stores it in a single column of type JSON, making it better suited for highly dynamic or nested JSON payloads.
sample.json file with the following newline-delimited JSON:
a as a Nullable(String).
Type coercionThis type coercion can be controlled through a number of settings. The above example is dependent on the setting
input_format_json_read_numbers_as_strings.DESCRIBE command thus fails:
JSONAsObject considers each row as a single JSON type (which supports the same column having multiple types). This is essential: