ClickHouse peut déterminer automatiquement la structure des données d’entrée dans presque tous les formats d’entrée.
Ce document explique quand l’inférence de schéma est utilisée, comment elle fonctionne avec différents formats d’entrée et quels paramètres
permettent de la contrôler.
L’inférence de schéma est utilisée lorsque ClickHouse doit lire des données dans un format spécifique et que la structure est inconnue.
Ces fonctions de table incluent l’argument facultatif structure, qui définit la structure des données d’entrée. Si cet argument n’est pas spécifié ou s’il est défini sur auto, la structure sera inférée à partir des données.
Exemple :
Supposons que nous ayons un fichier hobbies.jsonl au format JSONEachRow dans le répertoire user_files, avec le contenu suivant :
{"id" : 1, "age" : 25, "name" : "Josh", "hobbies" : ["football", "cooking", "music"]}
{"id" : 2, "age" : 19, "name" : "Alan", "hobbies" : ["tennis", "art"]}
{"id" : 3, "age" : 32, "name" : "Lana", "hobbies" : ["fitness", "reading", "shopping"]}
{"id" : 4, "age" : 47, "name" : "Brayan", "hobbies" : ["movies", "skydiving"]}
ClickHouse peut lire ces données sans que vous ayez à préciser leur structure :
SELECT * FROM file('hobbies.jsonl')
┌─id─┬─age─┬─name───┬─hobbies──────────────────────────┐
│ 1 │ 25 │ Josh │ ['football','cooking','music'] │
│ 2 │ 19 │ Alan │ ['tennis','art'] │
│ 3 │ 32 │ Lana │ ['fitness','reading','shopping'] │
│ 4 │ 47 │ Brayan │ ['movies','skydiving'] │
└────┴─────┴────────┴──────────────────────────────────┘
Remarque : le format JSONEachRow a été détecté automatiquement à partir de l’extension de fichier .jsonl.
Vous pouvez afficher la structure détectée automatiquement à l’aide de la requête DESCRIBE :
DESCRIBE file('hobbies.jsonl')
┌─name────┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ id │ Nullable(Int64) │ │ │ │ │ │
│ age │ Nullable(Int64) │ │ │ │ │ │
│ name │ Nullable(String) │ │ │ │ │ │
│ hobbies │ Array(Nullable(String)) │ │ │ │ │ │
└─────────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si la liste des colonnes n’est pas spécifiée dans la requête CREATE TABLE, la structure de la table sera automatiquement inférée à partir des données.
Exemple :
Utilisons le fichier hobbies.jsonl. Nous pouvons créer une table avec le moteur File à partir des données de ce fichier :
CREATE TABLE hobbies ENGINE=File(JSONEachRow, 'hobbies.jsonl')
┌─id─┬─age─┬─name───┬─hobbies──────────────────────────┐
│ 1 │ 25 │ Josh │ ['football','cooking','music'] │
│ 2 │ 19 │ Alan │ ['tennis','art'] │
│ 3 │ 32 │ Lana │ ['fitness','reading','shopping'] │
│ 4 │ 47 │ Brayan │ ['movies','skydiving'] │
└────┴─────┴────────┴──────────────────────────────────┘
┌─name────┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ id │ Nullable(Int64) │ │ │ │ │ │
│ age │ Nullable(Int64) │ │ │ │ │ │
│ name │ Nullable(String) │ │ │ │ │ │
│ hobbies │ Array(Nullable(String)) │ │ │ │ │ │
└─────────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
clickhouse-local dispose d’un paramètre facultatif -S/--structure permettant de définir la structure des données d’entrée. Si ce paramètre n’est pas renseigné ou s’il est défini sur auto, la structure sera inférée à partir des données.
Exemple :
Utilisons le fichier hobbies.jsonl. Nous pouvons interroger les données de ce fichier avec clickhouse-local :
clickhouse-local --file='hobbies.jsonl' --table='hobbies' --query='DESCRIBE TABLE hobbies'
id Nullable(Int64)
age Nullable(Int64)
name Nullable(String)
hobbies Array(Nullable(String))
clickhouse-local --file='hobbies.jsonl' --table='hobbies' --query='SELECT * FROM hobbies'
1 25 Josh ['football','cooking','music']
2 19 Alan ['tennis','art']
3 32 Lana ['fitness','reading','shopping']
4 47 Brayan ['movies','skydiving']
Utilisation de la structure de la table d’insertion
Lorsque les fonctions de table file/s3/url/hdfs sont utilisées pour insérer des données dans une table,
il est possible d’utiliser la structure de la table d’insertion au lieu de l’extraire des données.
Cela peut améliorer les performances d’insertion, car l’inférence de schéma peut prendre un certain temps. Cela est également utile lorsque la table possède un schéma optimisé, car
aucune conversion de type ne sera alors effectuée.
Il existe un paramètre spécial use_structure_from_insertion_table_in_table_functions
qui contrôle ce comportement. Il a 3 valeurs possibles :
- 0 - la fonction de table extraira la structure des données.
- 1 - la fonction de table utilisera la structure de la table d’insertion.
- 2 - ClickHouse déterminera automatiquement s’il est possible d’utiliser la structure de la table d’insertion ou de recourir à l’inférence de schéma. Valeur par défaut.
Exemple 1 :
Créons la table hobbies1 avec la structure suivante :
CREATE TABLE hobbies1
(
`id` UInt64,
`age` LowCardinality(UInt8),
`name` String,
`hobbies` Array(String)
)
ENGINE = MergeTree
ORDER BY id;
Et insérez les données du fichier hobbies.jsonl :
INSERT INTO hobbies1 SELECT * FROM file(hobbies.jsonl)
Dans ce cas, toutes les colonnes du fichier sont insérées dans la table sans modification ; ClickHouse utilisera donc la structure de la table d’insertion au lieu de l’inférence de schéma.
Exemple 2 :
Créons la table hobbies2 avec la structure suivante :
CREATE TABLE hobbies2
(
`id` UInt64,
`age` LowCardinality(UInt8),
`hobbies` Array(String)
)
ENGINE = MergeTree
ORDER BY id;
Et insérez les données du fichier hobbies.jsonl :
INSERT INTO hobbies2 SELECT id, age, hobbies FROM file(hobbies.jsonl)
Dans ce cas, toutes les colonnes de la requête SELECT sont présentes dans la table, donc ClickHouse utilisera la structure de la table d’insertion.
Notez que cela ne fonctionne que pour les formats d’entrée qui prennent en charge la lecture d’un sous-ensemble de colonnes, comme JSONEachRow, TSKV, Parquet, etc. (cela ne fonctionne donc pas, par exemple, avec le format TSV).
Exemple 3 :
Créons la table hobbies3 avec la structure suivante :
CREATE TABLE hobbies3
(
`identifier` UInt64,
`age` LowCardinality(UInt8),
`hobbies` Array(String)
)
ENGINE = MergeTree
ORDER BY identifier;
Et insérez les données à partir du fichier hobbies.jsonl :
INSERT INTO hobbies3 SELECT id, age, hobbies FROM file(hobbies.jsonl)
Dans ce cas, la colonne id est utilisée dans la requête SELECT, mais la table ne contient pas cette colonne (elle contient une colonne nommée identifier) ;
ClickHouse ne peut donc pas utiliser la structure de la table d’insertion, et l’inférence de schéma sera utilisée.
Exemple 4 :
Créons la table hobbies4 avec la structure suivante :
CREATE TABLE hobbies4
(
`id` UInt64,
`any_hobby` Nullable(String)
)
ENGINE = MergeTree
ORDER BY id;
Puis insérez des données depuis le fichier hobbies.jsonl :
INSERT INTO hobbies4 SELECT id, empty(hobbies) ? NULL : hobbies[1] FROM file(hobbies.jsonl)
Dans ce cas, certaines opérations sont appliquées à la colonne hobbies dans la requête SELECT avant son insertion dans la table ; ClickHouse ne peut donc pas utiliser la structure de la table d’insertion, et l’inférence de schéma sera utilisée.
Cache d’inférence de schéma
Pour la plupart des formats d’entrée, l’inférence de schéma lit une partie des données pour en déterminer la structure, et ce processus peut prendre un certain temps.
Pour éviter de réinférer le même schéma chaque fois que ClickHouse lit les données du même fichier, le schéma inféré est mis en cache et, lors d’un nouvel accès au même fichier, ClickHouse utilise le schéma du cache.
Il existe des paramètres spéciaux qui contrôlent ce cache :
schema_inference_cache_max_elements_for_{file/s3/hdfs/url/azure} - le nombre maximal de schémas mis en cache pour la fonction de table correspondante. La valeur par défaut est 4096. Ces paramètres doivent être définis dans la configuration du serveur.
schema_inference_use_cache_for_{file,s3,hdfs,url,azure} - permet d’activer ou de désactiver l’utilisation du cache pour l’inférence de schéma. Ces paramètres peuvent être utilisés dans les requêtes.
Le schéma du fichier peut être modifié en changeant les données ou les paramètres de format.
Pour cette raison, le cache d’inférence de schéma identifie le schéma à partir de la source du fichier, du nom du format, des paramètres de format utilisés et de la date de dernière modification du fichier.
Remarque : certains fichiers accessibles par URL dans la fonction de table url peuvent ne pas contenir d’informations sur la date de dernière modification ; dans ce cas, il existe un paramètre spécial
schema_inference_cache_require_modification_time_for_url. Désactiver ce paramètre permet d’utiliser le schéma du cache sans date de dernière modification pour ces fichiers.
Il existe également une table système schema_inference_cache contenant tous les schémas actuellement présents dans le cache, ainsi que la requête système SYSTEM CLEAR SCHEMA CACHE [FOR File/S3/URL/HDFS]
qui permet de vider le cache d’inférence de schéma pour toutes les sources ou pour une source spécifique.
Exemples :
Essayons d’inférer la structure d’un jeu de données d’exemple depuis S3 github-2022.ndjson.gz et voyons comment fonctionne le cache d’inférence de schéma :
DESCRIBE TABLE s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/github/github-2022.ndjson.gz')
┌─name───────┬─type─────────────────────────────────────────┐
│ type │ Nullable(String) │
│ actor │ Tuple( ↴│
│ │↳ avatar_url Nullable(String), ↴│
│ │↳ display_login Nullable(String), ↴│
│ │↳ id Nullable(Int64), ↴│
│ │↳ login Nullable(String), ↴│
│ │↳ url Nullable(String)) │
│ repo │ Tuple( ↴│
│ │↳ id Nullable(Int64), ↴│
│ │↳ name Nullable(String), ↴│
│ │↳ url Nullable(String)) │
│ created_at │ Nullable(String) │
│ payload │ Tuple( ↴│
│ │↳ action Nullable(String), ↴│
│ │↳ distinct_size Nullable(Int64), ↴│
│ │↳ pull_request Tuple( ↴│
│ │↳ author_association Nullable(String),↴│
│ │↳ base Tuple( ↴│
│ │↳ ref Nullable(String), ↴│
│ │↳ sha Nullable(String)), ↴│
│ │↳ head Tuple( ↴│
│ │↳ ref Nullable(String), ↴│
│ │↳ sha Nullable(String)), ↴│
│ │↳ number Nullable(Int64), ↴│
│ │↳ state Nullable(String), ↴│
│ │↳ title Nullable(String), ↴│
│ │↳ updated_at Nullable(String), ↴│
│ │↳ user Tuple( ↴│
│ │↳ login Nullable(String))), ↴│
│ │↳ ref Nullable(String), ↴│
│ │↳ ref_type Nullable(String), ↴│
│ │↳ size Nullable(Int64)) │
└────────────┴──────────────────────────────────────────────┘
5 rows in set. Elapsed: 0.601 sec.
DESCRIBE TABLE s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/github/github-2022.ndjson.gz')
┌─name───────┬─type─────────────────────────────────────────┐
│ type │ Nullable(String) │
│ actor │ Tuple( ↴│
│ │↳ avatar_url Nullable(String), ↴│
│ │↳ display_login Nullable(String), ↴│
│ │↳ id Nullable(Int64), ↴│
│ │↳ login Nullable(String), ↴│
│ │↳ url Nullable(String)) │
│ repo │ Tuple( ↴│
│ │↳ id Nullable(Int64), ↴│
│ │↳ name Nullable(String), ↴│
│ │↳ url Nullable(String)) │
│ created_at │ Nullable(String) │
│ payload │ Tuple( ↴│
│ │↳ action Nullable(String), ↴│
│ │↳ distinct_size Nullable(Int64), ↴│
│ │↳ pull_request Tuple( ↴│
│ │↳ author_association Nullable(String),↴│
│ │↳ base Tuple( ↴│
│ │↳ ref Nullable(String), ↴│
│ │↳ sha Nullable(String)), ↴│
│ │↳ head Tuple( ↴│
│ │↳ ref Nullable(String), ↴│
│ │↳ sha Nullable(String)), ↴│
│ │↳ number Nullable(Int64), ↴│
│ │↳ state Nullable(String), ↴│
│ │↳ title Nullable(String), ↴│
│ │↳ updated_at Nullable(String), ↴│
│ │↳ user Tuple( ↴│
│ │↳ login Nullable(String))), ↴│
│ │↳ ref Nullable(String), ↴│
│ │↳ ref_type Nullable(String), ↴│
│ │↳ size Nullable(Int64)) │
└────────────┴──────────────────────────────────────────────┘
5 rows in set. Elapsed: 0.059 sec.
Comme vous pouvez le voir, la seconde requête s’est exécutée presque instantanément.
Essayons de modifier certains paramètres qui peuvent affecter le schéma inféré :
DESCRIBE TABLE s3('https://datasets-documentation.s3.eu-west-3.amazonaws.com/github/github-2022.ndjson.gz')
SETTINGS input_format_json_try_infer_named_tuples_from_objects=0, input_format_json_read_objects_as_strings = 1
┌─name───────┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ type │ Nullable(String) │ │ │ │ │ │
│ actor │ Nullable(String) │ │ │ │ │ │
│ repo │ Nullable(String) │ │ │ │ │ │
│ created_at │ Nullable(String) │ │ │ │ │ │
│ payload │ Nullable(String) │ │ │ │ │ │
└────────────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
5 rows in set. Elapsed: 0.611 sec
Comme vous pouvez le voir, le schéma mis en cache n’a pas été utilisé pour le même fichier, car le paramètre susceptible d’affecter le schéma inféré a été modifié.
Vérifions le contenu de la table system.schema_inference_cache :
SELECT schema, format, source FROM system.schema_inference_cache WHERE storage='S3'
┌─schema──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─format─┬─source───────────────────────────────────────────────────────────────────────────────────────────────────┐
│ type Nullable(String), actor Tuple(avatar_url Nullable(String), display_login Nullable(String), id Nullable(Int64), login Nullable(String), url Nullable(String)), repo Tuple(id Nullable(Int64), name Nullable(String), url Nullable(String)), created_at Nullable(String), payload Tuple(action Nullable(String), distinct_size Nullable(Int64), pull_request Tuple(author_association Nullable(String), base Tuple(ref Nullable(String), sha Nullable(String)), head Tuple(ref Nullable(String), sha Nullable(String)), number Nullable(Int64), state Nullable(String), title Nullable(String), updated_at Nullable(String), user Tuple(login Nullable(String))), ref Nullable(String), ref_type Nullable(String), size Nullable(Int64)) │ NDJSON │ datasets-documentation.s3.eu-west-3.amazonaws.com443/datasets-documentation/github/github-2022.ndjson.gz │
│ type Nullable(String), actor Nullable(String), repo Nullable(String), created_at Nullable(String), payload Nullable(String) │ NDJSON │ datasets-documentation.s3.eu-west-3.amazonaws.com443/datasets-documentation/github/github-2022.ndjson.gz │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Comme vous pouvez le constater, il existe deux schémas différents pour le même fichier.
Nous pouvons vider le cache des schémas à l’aide d’une requête SYSTEM :
SYSTEM CLEAR SCHEMA CACHE FOR S3
SELECT count() FROM system.schema_inference_cache WHERE storage='S3'
┌─count()─┐
│ 0 │
└─────────┘
Pour les formats textuels, ClickHouse lit les données ligne par ligne, extrait les valeurs des colonnes selon le format,
puis utilise des parseurs récursifs et des heuristiques pour déterminer le type de chaque valeur. Le nombre maximal de lignes et d’octets lus dans les données lors de l’inférence de schéma
est contrôlé par les paramètres input_format_max_rows_to_read_for_schema_inference (25000 par défaut) et input_format_max_bytes_to_read_for_schema_inference (32Mb par défaut).
Par défaut, tous les types inférés sont Nullable, mais vous pouvez modifier ce comportement en définissant schema_inference_make_columns_nullable (voir des exemples dans la section paramètres).
Dans les formats JSON, ClickHouse analyse les valeurs conformément à la spécification JSON, puis tente de déterminer le type de données le plus approprié pour chacune d’elles.
Voyons comment cela fonctionne, quels types peuvent être inférés et quels paramètres spécifiques peuvent être utilisés dans les formats JSON.
Exemples
Ici et dans la suite, la table function format sera utilisée dans les exemples.
Integers, Floats, Bools, Strings :
DESC format(JSONEachRow, '{"int" : 42, "float" : 42.42, "string" : "Hello, World!"}');
┌─name───┬─type──────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ int │ Nullable(Int64) │ │ │ │ │ │
│ float │ Nullable(Float64) │ │ │ │ │ │
│ bool │ Nullable(Bool) │ │ │ │ │ │
│ string │ Nullable(String) │ │ │ │ │ │
└────────┴───────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Dates, DateTimes :
DESC format(JSONEachRow, '{"date" : "2022-01-01", "datetime" : "2022-01-01 00:00:00", "datetime64" : "2022-01-01 00:00:00.000"}')
┌─name───────┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ date │ Nullable(Date) │ │ │ │ │ │
│ datetime │ Nullable(DateTime) │ │ │ │ │ │
│ datetime64 │ Nullable(DateTime64(9)) │ │ │ │ │ │
└────────────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Arrays :
DESC format(JSONEachRow, '{"arr" : [1, 2, 3], "nested_arrays" : [[1, 2, 3], [4, 5, 6], []]}')
┌─name──────────┬─type──────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ arr │ Array(Nullable(Int64)) │ │ │ │ │ │
│ nested_arrays │ Array(Array(Nullable(Int64))) │ │ │ │ │ │
└───────────────┴───────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si un tableau contient null, ClickHouse utilisera les types des autres éléments du tableau :
DESC format(JSONEachRow, '{"arr" : [null, 42, null]}')
┌─name─┬─type───────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ arr │ Array(Nullable(Int64)) │ │ │ │ │ │
└──────┴────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si un tableau contient des valeurs de types différents et que le paramètre input_format_json_infer_array_of_dynamic_from_array_of_different_types est activé (activé par défaut), il aura le type Array(Dynamic) :
SET input_format_json_infer_array_of_dynamic_from_array_of_different_types=1;
DESC format(JSONEachRow, '{"arr" : [42, "hello", [1, 2, 3]]}');
┌─name─┬─type───────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ arr │ Array(Dynamic) │ │ │ │ │ │
└──────┴────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Tuples nommés :
Lorsque le paramètre input_format_json_try_infer_named_tuples_from_objects est activé, ClickHouse tentera, lors de l’inférence de schéma, d’inférer un Tuple nommé à partir d’objets JSON.
Le Tuple nommé obtenu contiendra tous les éléments de tous les objets JSON correspondants présents dans les données d’échantillon.
SET input_format_json_try_infer_named_tuples_from_objects = 1;
DESC format(JSONEachRow, '{"obj" : {"a" : 42, "b" : "Hello"}}, {"obj" : {"a" : 43, "c" : [1, 2, 3]}}, {"obj" : {"d" : {"e" : 42}}}')
┌─name─┬─type───────────────────────────────────────────────────────────────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ obj │ Tuple(a Nullable(Int64), b Nullable(String), c Array(Nullable(Int64)), d Tuple(e Nullable(Int64))) │ │ │ │ │ │
└──────┴────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Tuples non nommés :
Si le paramètre input_format_json_infer_array_of_dynamic_from_array_of_different_types est désactivé, les Arrays dont les éléments sont de types différents sont traités comme des Tuples non nommés dans les formats JSON.
SET input_format_json_infer_array_of_dynamic_from_array_of_different_types = 0;
DESC format(JSONEachRow, '{"tuple" : [1, "Hello, World!", [1, 2, 3]]}')
┌─name──┬─type─────────────────────────────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ tuple │ Tuple(Nullable(Int64), Nullable(String), Array(Nullable(Int64))) │ │ │ │ │ │
└───────┴──────────────────────────────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si certaines valeurs sont null ou vides, nous utilisons les types des valeurs correspondantes présentes dans les autres lignes :
SET input_format_json_infer_array_of_dynamic_from_array_of_different_types=0;
DESC format(JSONEachRow, $$
{"tuple" : [1, null, null]}
{"tuple" : [null, "Hello, World!", []]}
{"tuple" : [null, null, [1, 2, 3]]}
$$)
┌─name──┬─type─────────────────────────────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ tuple │ Tuple(Nullable(Int64), Nullable(String), Array(Nullable(Int64))) │ │ │ │ │ │
└───────┴──────────────────────────────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Maps :
En JSON, il est possible de lire des objets dont les valeurs sont du même type que le type Map.
Remarque : cela ne fonctionne que lorsque les paramètres input_format_json_read_objects_as_strings et input_format_json_try_infer_named_tuples_from_objects sont désactivés.
SET input_format_json_read_objects_as_strings = 0, input_format_json_try_infer_named_tuples_from_objects = 0;
DESC format(JSONEachRow, '{"map" : {"key1" : 42, "key2" : 24, "key3" : 4}}')
┌─name─┬─type─────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ map │ Map(String, Nullable(Int64)) │ │ │ │ │ │
└──────┴──────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Types complexes Nested :
DESC format(JSONEachRow, '{"value" : [[[42, 24], []], {"key1" : 42, "key2" : 24}]}')
┌─name──┬─type─────────────────────────────────────────────────────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ value │ Tuple(Array(Array(Nullable(String))), Tuple(key1 Nullable(Int64), key2 Nullable(Int64))) │ │ │ │ │ │
└───────┴──────────────────────────────────────────────────────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si ClickHouse ne peut pas déterminer le type d’une clé parce que les données ne contiennent que des valeurs nulles/objets vides/tableaux vides, le type String sera utilisé si le paramètre input_format_json_infer_incomplete_types_as_strings est activé ; sinon, une exception sera levée :
DESC format(JSONEachRow, '{"arr" : [null, null]}') SETTINGS input_format_json_infer_incomplete_types_as_strings = 1;
┌─name─┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ arr │ Array(Nullable(String)) │ │ │ │ │ │
└──────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
DESC format(JSONEachRow, '{"arr" : [null, null]}') SETTINGS input_format_json_infer_incomplete_types_as_strings = 0;
Code: 652. DB::Exception: Received from localhost:9000. DB::Exception:
Cannot determine type for column 'arr' by first 1 rows of data,
most likely this column contains only Nulls or empty Arrays/Maps.
...
input_format_json_try_infer_numbers_from_strings
L’activation de ce paramètre permet de déduire des nombres à partir de valeurs de type chaîne.
Ce paramètre est désactivé par défaut.
Exemple :
SET input_format_json_try_infer_numbers_from_strings = 1;
DESC format(JSONEachRow, $$
{"value" : "42"}
{"value" : "424242424242"}
$$)
┌─name──┬─type────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ value │ Nullable(Int64) │ │ │ │ │ │
└───────┴─────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
input_format_json_try_infer_named_tuples_from_objects
L’activation de ce paramètre permet d’inférer des tuples nommés à partir d’objets JSON. Le tuple nommé obtenu contiendra tous les éléments de tous les objets JSON correspondants présents dans l’échantillon de données.
Cela peut être utile lorsque les données JSON ne sont pas clairsemées, de sorte que l’échantillon de données contienne toutes les clés d’objet possibles.
Ce paramètre est activé par défaut.
Exemple
SET input_format_json_try_infer_named_tuples_from_objects = 1;
DESC format(JSONEachRow, '{"obj" : {"a" : 42, "b" : "Hello"}}, {"obj" : {"a" : 43, "c" : [1, 2, 3]}}, {"obj" : {"d" : {"e" : 42}}}')
┌─name─┬─type───────────────────────────────────────────────────────────────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ obj │ Tuple(a Nullable(Int64), b Nullable(String), c Array(Nullable(Int64)), d Tuple(e Nullable(Int64))) │ │ │ │ │ │
└──────┴────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
SET input_format_json_try_infer_named_tuples_from_objects = 1;
DESC format(JSONEachRow, '{"array" : [{"a" : 42, "b" : "Hello"}, {}, {"c" : [1,2,3]}, {"d" : "2020-01-01"}]}')
┌─name──┬─type────────────────────────────────────────────────────────────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ array │ Array(Tuple(a Nullable(Int64), b Nullable(String), c Array(Nullable(Int64)), d Nullable(Date))) │ │ │ │ │ │
└───────┴─────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
input_format_json_use_string_type_for_ambiguous_paths_in_named_tuples_inference_from_objects
L’activation de ce paramètre permet d’utiliser le type String pour les chemins ambigus lors de l’inférence de tuples nommés à partir d’objets JSON (lorsque input_format_json_try_infer_named_tuples_from_objects est activé), au lieu de lever une exception.
Cela permet de lire des objets JSON comme des tuples nommés même en présence de chemins ambigus.
Désactivé par défaut.
Exemples
Avec le paramètre désactivé :
SET input_format_json_try_infer_named_tuples_from_objects = 1;
SET input_format_json_use_string_type_for_ambiguous_paths_in_named_tuples_inference_from_objects = 0;
DESC format(JSONEachRow, '{"obj" : {"a" : 42}}, {"obj" : {"a" : {"b" : "Hello"}}}');
Code: 636. DB::Exception: The table structure cannot be extracted from a JSONEachRow format file. Error:
Code: 117. DB::Exception: JSON objects have ambiguous data: in some objects path 'a' has type 'Int64' and in some - 'Tuple(b String)'. You can enable setting input_format_json_use_string_type_for_ambiguous_paths_in_named_tuples_inference_from_objects to use String type for path 'a'. (INCORRECT_DATA) (version 24.3.1.1).
You can specify the structure manually. (CANNOT_EXTRACT_TABLE_STRUCTURE)
Lorsque ce paramètre est activé :
SET input_format_json_try_infer_named_tuples_from_objects = 1;
SET input_format_json_use_string_type_for_ambiguous_paths_in_named_tuples_inference_from_objects = 1;
DESC format(JSONEachRow, '{"obj" : "a" : 42}, {"obj" : {"a" : {"b" : "Hello"}}}');
SELECT * FROM format(JSONEachRow, '{"obj" : {"a" : 42}}, {"obj" : {"a" : {"b" : "Hello"}}}');
┌─name─┬─type──────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ obj │ Tuple(a Nullable(String)) │ │ │ │ │ │
└──────┴───────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
┌─obj─────────────────┐
│ ('42') │
│ ('{"b" : "Hello"}') │
└─────────────────────┘
input_format_json_read_objects_as_strings
L’activation de ce paramètre permet de lire les objets JSON imbriqués sous forme de chaînes.
Ce paramètre peut être utilisé pour lire des objets JSON imbriqués sans utiliser le type JSON object.
Ce paramètre est activé par défaut.
Remarque : l’activation de ce paramètre ne prend effet que si le paramètre input_format_json_try_infer_named_tuples_from_objects est désactivé.
SET input_format_json_read_objects_as_strings = 1, input_format_json_try_infer_named_tuples_from_objects = 0;
DESC format(JSONEachRow, $$
{"obj" : {"key1" : 42, "key2" : [1,2,3,4]}}
{"obj" : {"key3" : {"nested_key" : 1}}}
$$)
┌─name─┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ obj │ Nullable(String) │ │ │ │ │ │
└──────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
input_format_json_read_numbers_as_strings
L’activation de ce paramètre permet de lire les valeurs numériques sous forme de chaînes.
Ce paramètre est activé par défaut.
Exemple
SET input_format_json_read_numbers_as_strings = 1;
DESC format(JSONEachRow, $$
{"value" : 1055}
{"value" : "unknown"}
$$)
┌─name──┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ value │ Nullable(String) │ │ │ │ │ │
└───────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
input_format_json_read_bools_as_numbers
L’activation de ce paramètre permet de lire les valeurs booléennes comme des nombres.
Ce paramètre est activé par défaut.
Exemple :
SET input_format_json_read_bools_as_numbers = 1;
DESC format(JSONEachRow, $$
{"value" : true}
{"value" : 42}
$$)
┌─name──┬─type────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ value │ Nullable(Int64) │ │ │ │ │ │
└───────┴─────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
input_format_json_read_bools_as_strings
L’activation de ce paramètre permet de lire les valeurs Bool sous forme de chaînes de caractères.
Ce paramètre est activé par défaut.
Exemple :
SET input_format_json_read_bools_as_strings = 1;
DESC format(JSONEachRow, $$
{"value" : true}
{"value" : "Hello, World"}
$$)
┌─name──┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ value │ Nullable(String) │ │ │ │ │ │
└───────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
input_format_json_read_arrays_as_strings
L’activation de ce paramètre permet de lire les valeurs des tableaux JSON sous forme de chaînes de caractères.
Ce paramètre est activé par défaut.
Exemple
SET input_format_json_read_arrays_as_strings = 1;
SELECT arr, toTypeName(arr), JSONExtractArrayRaw(arr)[3] from format(JSONEachRow, 'arr String', '{"arr" : [1, "Hello", [1,2,3]]}');
┌─arr───────────────────┬─toTypeName(arr)─┬─arrayElement(JSONExtractArrayRaw(arr), 3)─┐
│ [1, "Hello", [1,2,3]] │ String │ [1,2,3] │
└───────────────────────┴─────────────────┴───────────────────────────────────────────┘
input_format_json_infer_incomplete_types_as_strings
L’activation de ce paramètre permet d’utiliser le type String pour les clés JSON qui ne contiennent que Null/{}/[] dans l’échantillon de données lors de l’inférence de schéma.
Dans les formats JSON, toute valeur peut être lue comme String si tous les paramètres correspondants sont activés (ils le sont tous par défaut), ce qui permet d’éviter des erreurs comme Cannot determine type for column 'column_name' by first 25000 rows of data, most likely this column contains only Nulls or empty Arrays/Maps lors de l’inférence de schéma
en utilisant le type String pour les clés dont le type est inconnu.
Exemple :
SET input_format_json_infer_incomplete_types_as_strings = 1, input_format_json_try_infer_named_tuples_from_objects = 1;
DESCRIBE format(JSONEachRow, '{"obj" : {"a" : [1,2,3], "b" : "hello", "c" : null, "d" : {}, "e" : []}}');
SELECT * FROM format(JSONEachRow, '{"obj" : {"a" : [1,2,3], "b" : "hello", "c" : null, "d" : {}, "e" : []}}');
┌─name─┬─type───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ obj │ Tuple(a Array(Nullable(Int64)), b Nullable(String), c Nullable(String), d Nullable(String), e Array(Nullable(String))) │ │ │ │ │ │
└──────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
┌─obj────────────────────────────┐
│ ([1,2,3],'hello',NULL,'{}',[]) │
└────────────────────────────────┘
Dans le format CSV, ClickHouse extrait les valeurs de colonne à partir de la ligne en fonction des délimiteurs. ClickHouse s’attend à ce que tous les types, à l’exception des nombres et des chaînes, soient entourés de guillemets doubles. Si la valeur est entre guillemets doubles, ClickHouse essaie d’analyser
le contenu entre guillemets à l’aide du parseur récursif, puis essaie de déterminer le type de données le plus approprié. Si la valeur n’est pas entre guillemets doubles, ClickHouse essaie de l’analyser comme un nombre,
et si la valeur n’est pas un nombre, ClickHouse la traite comme une chaîne.
Si vous ne voulez pas que ClickHouse essaie de déterminer des types complexes à l’aide de certains parseurs et heuristiques, vous pouvez désactiver le paramètre input_format_csv_use_best_effort_in_schema_inference
et ClickHouse traitera alors toutes les colonnes comme des Strings.
Si le paramètre input_format_csv_detect_header est activé, ClickHouse essaiera de détecter l’en-tête avec les noms de colonnes (et éventuellement les types) lors de l’inférence du schéma. Ce paramètre est activé par défaut.
Exemples :
Entiers, Floats, Bools, Strings:
DESC format(CSV, '42,42.42,true,"Hello,World!"')
┌─name─┬─type──────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(Int64) │ │ │ │ │ │
│ c2 │ Nullable(Float64) │ │ │ │ │ │
│ c3 │ Nullable(Bool) │ │ │ │ │ │
│ c4 │ Nullable(String) │ │ │ │ │ │
└──────┴───────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Chaînes sans guillemets :
DESC format(CSV, 'Hello world!,World hello!')
┌─name─┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(String) │ │ │ │ │ │
│ c2 │ Nullable(String) │ │ │ │ │ │
└──────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Dates, DateTimes :
DESC format(CSV, '"2020-01-01","2020-01-01 00:00:00","2022-01-01 00:00:00.000"')
┌─name─┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(Date) │ │ │ │ │ │
│ c2 │ Nullable(DateTime) │ │ │ │ │ │
│ c3 │ Nullable(DateTime64(9)) │ │ │ │ │ │
└──────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Tableaux :
DESC format(CSV, '"[1,2,3]","[[1, 2], [], [3, 4]]"')
┌─name─┬─type──────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Nullable(Int64)) │ │ │ │ │ │
│ c2 │ Array(Array(Nullable(Int64))) │ │ │ │ │ │
└──────┴───────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
DESC format(CSV, $$"['Hello', 'world']","[['Abc', 'Def'], []]"$$)
┌─name─┬─type───────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Nullable(String)) │ │ │ │ │ │
│ c2 │ Array(Array(Nullable(String))) │ │ │ │ │ │
└──────┴────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si un tableau contient la valeur NULL, ClickHouse utilisera les types des autres éléments du tableau :
DESC format(CSV, '"[NULL, 42, NULL]"')
┌─name─┬─type───────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Nullable(Int64)) │ │ │ │ │ │
└──────┴────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Maps :
DESC format(CSV, $$"{'key1' : 42, 'key2' : 24}"$$)
┌─name─┬─type─────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Map(String, Nullable(Int64)) │ │ │ │ │ │
└──────┴──────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Nested, tableaux et Maps :
DESC format(CSV, $$"[{'key1' : [[42, 42], []], 'key2' : [[null], [42]]}]"$$)
┌─name─┬─type──────────────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Map(String, Array(Array(Nullable(Int64))))) │ │ │ │ │ │
└──────┴───────────────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si ClickHouse ne peut pas déterminer le type entre guillemets parce que les données ne contiennent que des valeurs NULL, ClickHouse le traitera comme String :
DESC format(CSV, '"[NULL, NULL]"')
┌─name─┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(String) │ │ │ │ │ │
└──────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Exemple avec le paramètre input_format_csv_use_best_effort_in_schema_inference désactivé :
SET input_format_csv_use_best_effort_in_schema_inference = 0
DESC format(CSV, '"[1,2,3]",42.42,Hello World!')
┌─name─┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(String) │ │ │ │ │ │
│ c2 │ Nullable(String) │ │ │ │ │ │
│ c3 │ Nullable(String) │ │ │ │ │ │
└──────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Exemples de détection automatique de l’en-tête (lorsque input_format_csv_detect_header est activé) :
Noms uniquement :
SELECT * FROM format(CSV,
$$"number","string","array"
42,"Hello","[1, 2, 3]"
43,"World","[4, 5, 6]"
$$)
┌─number─┬─string─┬─array───┐
│ 42 │ Hello │ [1,2,3] │
│ 43 │ World │ [4,5,6] │
└────────┴────────┴─────────┘
Noms et types :
DESC format(CSV,
$$"number","string","array"
"UInt32","String","Array(UInt16)"
42,"Hello","[1, 2, 3]"
43,"World","[4, 5, 6]"
$$)
┌─name───┬─type──────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ number │ UInt32 │ │ │ │ │ │
│ string │ String │ │ │ │ │ │
│ array │ Array(UInt16) │ │ │ │ │ │
└────────┴───────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Notez que l’en-tête ne peut être détecté que s’il existe au moins une colonne d’un type autre que String. Si toutes les colonnes sont de type String, l’en-tête n’est pas détecté :
SELECT * FROM format(CSV,
$$"first_column","second_column"
"Hello","World"
"World","Hello"
$$)
┌─c1───────────┬─c2────────────┐
│ first_column │ second_column │
│ Hello │ World │
│ World │ Hello │
└──────────────┴───────────────┘
input_format_csv_try_infer_numbers_from_strings
L’activation de ce paramètre permet d’inférer des nombres à partir de valeurs de chaîne de caractères.
Ce paramètre est désactivé par défaut.
Exemple :
SET input_format_json_try_infer_numbers_from_strings = 1;
DESC format(CSV, '42,42.42');
┌─name─┬─type──────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(Int64) │ │ │ │ │ │
│ c2 │ Nullable(Float64) │ │ │ │ │ │
└──────┴───────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Dans les formats TSV/TSKV, ClickHouse extrait la valeur de la colonne à partir de la ligne en fonction des délimiteurs de tabulation, puis analyse la valeur extraite à l’aide
du parseur récursif afin de déterminer le type le plus approprié. Si le type ne peut pas être déterminé, ClickHouse traite cette valeur comme String.
Si vous ne souhaitez pas que ClickHouse tente de déterminer des types complexes à l’aide de certains parseurs et heuristiques, vous pouvez désactiver le paramètre input_format_tsv_use_best_effort_in_schema_inference
et ClickHouse traitera alors toutes les colonnes comme des Strings.
Si le paramètre input_format_tsv_detect_header est activé, ClickHouse essaiera de détecter l’en-tête contenant les noms de colonnes (et éventuellement les types) lors de l’inférence du schéma. Ce paramètre est activé par défaut.
Exemples :
Entiers, Floats, Bools, Strings:
DESC format(TSV, '42 42.42 true Hello,World!')
┌─name─┬─type──────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(Int64) │ │ │ │ │ │
│ c2 │ Nullable(Float64) │ │ │ │ │ │
│ c3 │ Nullable(Bool) │ │ │ │ │ │
│ c4 │ Nullable(String) │ │ │ │ │ │
└──────┴───────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
DESC format(TSKV, 'int=42 float=42.42 bool=true string=Hello,World!\n')
┌─name───┬─type──────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ int │ Nullable(Int64) │ │ │ │ │ │
│ float │ Nullable(Float64) │ │ │ │ │ │
│ bool │ Nullable(Bool) │ │ │ │ │ │
│ string │ Nullable(String) │ │ │ │ │ │
└────────┴───────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Dates, DateTimes:
DESC format(TSV, '2020-01-01 2020-01-01 00:00:00 2022-01-01 00:00:00.000')
┌─name─┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(Date) │ │ │ │ │ │
│ c2 │ Nullable(DateTime) │ │ │ │ │ │
│ c3 │ Nullable(DateTime64(9)) │ │ │ │ │ │
└──────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
tableaux :
DESC format(TSV, '[1,2,3] [[1, 2], [], [3, 4]]')
┌─name─┬─type──────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Nullable(Int64)) │ │ │ │ │ │
│ c2 │ Array(Array(Nullable(Int64))) │ │ │ │ │ │
└──────┴───────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
DESC format(TSV, '[''Hello'', ''world''] [[''Abc'', ''Def''], []]')
┌─name─┬─type───────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Nullable(String)) │ │ │ │ │ │
│ c2 │ Array(Array(Nullable(String))) │ │ │ │ │ │
└──────┴────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si un tableau contient NULL, ClickHouse utilisera les types des autres éléments du tableau :
DESC format(TSV, '[NULL, 42, NULL]')
┌─name─┬─type───────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Nullable(Int64)) │ │ │ │ │ │
└──────┴────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Tuples :
DESC format(TSV, $$(42, 'Hello, world!')$$)
┌─name─┬─type─────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Tuple(Nullable(Int64), Nullable(String)) │ │ │ │ │ │
└──────┴──────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Maps :
DESC format(TSV, $${'key1' : 42, 'key2' : 24}$$)
┌─name─┬─type─────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Map(String, Nullable(Int64)) │ │ │ │ │ │
└──────┴──────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Nested tableaux, Tuples et Maps :
DESC format(TSV, $$[{'key1' : [(42, 'Hello'), (24, NULL)], 'key2' : [(NULL, ','), (42, 'world!')]}]$$)
┌─name─┬─type────────────────────────────────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Map(String, Array(Tuple(Nullable(Int64), Nullable(String))))) │ │ │ │ │ │
└──────┴─────────────────────────────────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si ClickHouse ne peut pas déterminer le type parce que les données ne contiennent que des valeurs NULL, ClickHouse le traitera comme String :
DESC format(TSV, '[NULL, NULL]')
┌─name─┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(String) │ │ │ │ │ │
└──────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Exemple avec le paramètre input_format_tsv_use_best_effort_in_schema_inference désactivé :
SET input_format_tsv_use_best_effort_in_schema_inference = 0
DESC format(TSV, '[1,2,3] 42.42 Hello World!')
┌─name─┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(String) │ │ │ │ │ │
│ c2 │ Nullable(String) │ │ │ │ │ │
│ c3 │ Nullable(String) │ │ │ │ │ │
└──────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Exemples de détection automatique de l’en-tête (lorsque input_format_tsv_detect_header est activé) :
Noms uniquement :
SELECT * FROM format(TSV,
$$number string array
42 Hello [1, 2, 3]
43 World [4, 5, 6]
$$);
┌─number─┬─string─┬─array───┐
│ 42 │ Hello │ [1,2,3] │
│ 43 │ World │ [4,5,6] │
└────────┴────────┴─────────┘
Noms et types :
DESC format(TSV,
$$number string array
UInt32 String Array(UInt16)
42 Hello [1, 2, 3]
43 World [4, 5, 6]
$$)
┌─name───┬─type──────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ number │ UInt32 │ │ │ │ │ │
│ string │ String │ │ │ │ │ │
│ array │ Array(UInt16) │ │ │ │ │ │
└────────┴───────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Notez que l’en-tête ne peut être détecté que s’il existe au moins une colonne d’un type autre que String. Si toutes les colonnes sont de type String, l’en-tête n’est pas détecté :
SELECT * FROM format(TSV,
$$first_column second_column
Hello World
World Hello
$$)
┌─c1───────────┬─c2────────────┐
│ first_column │ second_column │
│ Hello │ World │
│ World │ Hello │
└──────────────┴───────────────┘
Dans le format Values, ClickHouse extrait la valeur de la colonne depuis la ligne, puis l’analyse à l’aide du parseur récursif, de la même façon que les littéraux sont analysés.
Exemples :
Integers, Floats, Bools, Strings :
DESC format(Values, $$(42, 42.42, true, 'Hello,World!')$$)
┌─name─┬─type──────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(Int64) │ │ │ │ │ │
│ c2 │ Nullable(Float64) │ │ │ │ │ │
│ c3 │ Nullable(Bool) │ │ │ │ │ │
│ c4 │ Nullable(String) │ │ │ │ │ │
└──────┴───────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Dates, DateTimes :
DESC format(Values, $$('2020-01-01', '2020-01-01 00:00:00', '2022-01-01 00:00:00.000')$$)
┌─name─┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(Date) │ │ │ │ │ │
│ c2 │ Nullable(DateTime) │ │ │ │ │ │
│ c3 │ Nullable(DateTime64(9)) │ │ │ │ │ │
└──────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Arrays :
DESC format(Values, '([1,2,3], [[1, 2], [], [3, 4]])')
┌─name─┬─type──────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Nullable(Int64)) │ │ │ │ │ │
│ c2 │ Array(Array(Nullable(Int64))) │ │ │ │ │ │
└──────┴───────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si un tableau contient null, ClickHouse utilisera les types des autres éléments du tableau :
DESC format(Values, '([NULL, 42, NULL])')
┌─name─┬─type───────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Nullable(Int64)) │ │ │ │ │ │
└──────┴────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Tuples :
DESC format(Values, $$((42, 'Hello, world!'))$$)
┌─name─┬─type─────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Tuple(Nullable(Int64), Nullable(String)) │ │ │ │ │ │
└──────┴──────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Maps :
DESC format(Values, $$({'key1' : 42, 'key2' : 24})$$)
┌─name─┬─type─────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Map(String, Nullable(Int64)) │ │ │ │ │ │
└──────┴──────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Nested Arrays, Tuples et Maps :
DESC format(Values, $$([{'key1' : [(42, 'Hello'), (24, NULL)], 'key2' : [(NULL, ','), (42, 'world!')]}])$$)
┌─name─┬─type────────────────────────────────────────────────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Array(Map(String, Array(Tuple(Nullable(Int64), Nullable(String))))) │ │ │ │ │ │
└──────┴─────────────────────────────────────────────────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Si ClickHouse ne peut pas déterminer le type, car les données ne contiennent que des valeurs NULL, une exception sera levée :
DESC format(Values, '([NULL, NULL])')
Code: 652. DB::Exception: Received from localhost:9000. DB::Exception:
Cannot determine type for column 'c1' by first 1 rows of data,
most likely this column contains only Nulls or empty Arrays/Maps.
...
Exemple avec le paramètre input_format_tsv_use_best_effort_in_schema_inference désactivé :
SET input_format_tsv_use_best_effort_in_schema_inference = 0
DESC format(TSV, '[1,2,3] 42.42 Hello World!')
┌─name─┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(String) │ │ │ │ │ │
│ c2 │ Nullable(String) │ │ │ │ │ │
│ c3 │ Nullable(String) │ │ │ │ │ │
└──────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Dans le format CustomSeparated, ClickHouse extrait d’abord toutes les valeurs des colonnes de la ligne selon les délimiteurs spécifiés, puis tente d’inférer
le type de données de chaque valeur en fonction de la règle d’échappement.
Si le paramètre input_format_custom_detect_header est activé, ClickHouse essaiera de détecter l’en-tête contenant les noms de colonnes (et éventuellement les types) lors de l’inférence du schéma. Ce paramètre est activé par défaut.
Exemple
SET format_custom_row_before_delimiter = '<row_before_delimiter>',
format_custom_row_after_delimiter = '<row_after_delimiter>\n',
format_custom_row_between_delimiter = '<row_between_delimiter>\n',
format_custom_result_before_delimiter = '<result_before_delimiter>\n',
format_custom_result_after_delimiter = '<result_after_delimiter>\n',
format_custom_field_delimiter = '<field_delimiter>',
format_custom_escaping_rule = 'Quoted'
DESC format(CustomSeparated, $$<result_before_delimiter>
<row_before_delimiter>42.42<field_delimiter>'Some string 1'<field_delimiter>[1, NULL, 3]<row_after_delimiter>
<row_between_delimiter>
<row_before_delimiter>NULL<field_delimiter>'Some string 3'<field_delimiter>[1, 2, NULL]<row_after_delimiter>
<result_after_delimiter>
$$)
┌─name─┬─type───────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(Float64) │ │ │ │ │ │
│ c2 │ Nullable(String) │ │ │ │ │ │
│ c3 │ Array(Nullable(Int64)) │ │ │ │ │ │
└──────┴────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Exemple de détection automatique de l’en-tête (lorsque input_format_custom_detect_header est activé) :
SET format_custom_row_before_delimiter = '<row_before_delimiter>',
format_custom_row_after_delimiter = '<row_after_delimiter>\n',
format_custom_row_between_delimiter = '<row_between_delimiter>\n',
format_custom_result_before_delimiter = '<result_before_delimiter>\n',
format_custom_result_after_delimiter = '<result_after_delimiter>\n',
format_custom_field_delimiter = '<field_delimiter>',
format_custom_escaping_rule = 'Quoted'
DESC format(CustomSeparated, $$<result_before_delimiter>
<row_before_delimiter>'number'<field_delimiter>'string'<field_delimiter>'array'<row_after_delimiter>
<row_between_delimiter>
<row_before_delimiter>42.42<field_delimiter>'Some string 1'<field_delimiter>[1, NULL, 3]<row_after_delimiter>
<row_between_delimiter>
<row_before_delimiter>NULL<field_delimiter>'Some string 3'<field_delimiter>[1, 2, NULL]<row_after_delimiter>
<result_after_delimiter>
$$)
┌─number─┬─string────────┬─array──────┐
│ 42.42 │ Some string 1 │ [1,NULL,3] │
│ ᴺᵁᴸᴸ │ Some string 3 │ [1,2,NULL] │
└────────┴───────────────┴────────────┘
Dans le format Template, ClickHouse extrait d’abord de la ligne les valeurs de toutes les colonnes selon le modèle spécifié, puis essaie d’inférer le type de données de chaque valeur en fonction de sa règle d’échappement.
Exemple
Supposons que nous ayons un fichier resultset avec le contenu suivant :
<result_before_delimiter>
${data}<result_after_delimiter>
Et un fichier row_format avec le contenu suivant :
<row_before_delimiter>${column_1:CSV}<field_delimiter_1>${column_2:Quoted}<field_delimiter_2>${column_3:JSON}<row_after_delimiter>
Nous pouvons ensuite exécuter les requêtes suivantes :
SET format_template_rows_between_delimiter = '<row_between_delimiter>\n',
format_template_row = 'row_format',
format_template_resultset = 'resultset_format'
DESC format(Template, $$<result_before_delimiter>
<row_before_delimiter>42.42<field_delimiter_1>'Some string 1'<field_delimiter_2>[1, null, 2]<row_after_delimiter>
<row_between_delimiter>
<row_before_delimiter>\N<field_delimiter_1>'Some string 3'<field_delimiter_2>[1, 2, null]<row_after_delimiter>
<result_after_delimiter>
$$)
┌─name─────┬─type───────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ column_1 │ Nullable(Float64) │ │ │ │ │ │
│ column_2 │ Nullable(String) │ │ │ │ │ │
│ column_3 │ Array(Nullable(Int64)) │ │ │ │ │ │
└──────────┴────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Comme avec Template, dans le format Regexp, ClickHouse extrait d’abord de la ligne toutes les valeurs des colonnes selon l’expression régulière spécifiée, puis tente d’inférer
le type de données de chaque valeur selon la règle d’échappement spécifiée.
Exemple
SET format_regexp = '^Line: value_1=(.+?), value_2=(.+?), value_3=(.+?)',
format_regexp_escaping_rule = 'CSV'
DESC format(Regexp, $$Line: value_1=42, value_2="Some string 1", value_3="[1, NULL, 3]"
Line: value_1=2, value_2="Some string 2", value_3="[4, 5, NULL]"$$)
┌─name─┬─type───────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(Int64) │ │ │ │ │ │
│ c2 │ Nullable(String) │ │ │ │ │ │
│ c3 │ Array(Nullable(Int64)) │ │ │ │ │ │
└──────┴────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Paramètres des formats texte
Ces paramètres contrôlent la quantité de données à lire lors de l’inférence du schéma.
Plus le nombre de lignes ou d’octets lus est élevé, plus l’inférence du schéma prend du temps, mais plus les chances de
déterminer correctement les types sont élevées (en particulier lorsque les données contiennent beaucoup de valeurs NULL).
Valeurs par défaut :
25000 pour input_format_max_rows_to_read_for_schema_inference.
33554432 (32 Mb) pour input_format_max_bytes_to_read_for_schema_inference.
column_names_for_schema_inference
La liste des noms de colonnes à utiliser pour l’inférence du schéma dans les formats sans noms de colonnes explicites. Les noms spécifiés seront utilisés à la place des noms par défaut c1,c2,c3,.... Format : column1,column2,column3,....
Exemple
DESC format(TSV, 'Hello, World! 42 [1, 2, 3]') settings column_names_for_schema_inference = 'str,int,arr'
┌─name─┬─type───────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ str │ Nullable(String) │ │ │ │ │ │
│ int │ Nullable(Int64) │ │ │ │ │ │
│ arr │ Array(Nullable(Int64)) │ │ │ │ │ │
└──────┴────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
La liste des noms et types de colonnes à utiliser pour l’inférence du schéma à la place des types déterminés automatiquement. Format : ‘column_name1 column_type1, column_name2 column_type2, …’.
Ce paramètre peut être utilisé pour spécifier les types de colonnes qui n’ont pas pu être déterminés automatiquement ou pour optimiser le schéma.
Exemple
DESC format(JSONEachRow, '{"id" : 1, "age" : 25, "name" : "Josh", "status" : null, "hobbies" : ["football", "cooking"]}') SETTINGS schema_inference_hints = 'age LowCardinality(UInt8), status Nullable(String)', allow_suspicious_low_cardinality_types=1
┌─name────┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ id │ Nullable(Int64) │ │ │ │ │ │
│ age │ LowCardinality(UInt8) │ │ │ │ │ │
│ name │ Nullable(String) │ │ │ │ │ │
│ status │ Nullable(String) │ │ │ │ │ │
│ hobbies │ Array(Nullable(String)) │ │ │ │ │ │
└─────────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
schema_inference_make_columns_nullable $
Contrôle si les types inférés sont rendus Nullable lors de l’inférence de schéma pour les formats ne contenant pas d’information sur la nullabilité. Valeurs possibles :
- 0 - le type inféré ne sera jamais
Nullable,
- 1 - tous les types inférés seront
Nullable,
- 2 ou ‘auto’ - pour les formats texte, le type inféré sera
Nullable uniquement si la colonne contient NULL dans un échantillon analysé lors de l’inférence du schéma ; pour les formats fortement typés (Parquet, ORC, Arrow), les informations de nullabilité sont extraites des métadonnées du fichier,
- 3 - pour les formats texte, utilisez
Nullable ; pour les formats fortement typés, utilisez les métadonnées du fichier.
Par défaut : 3.
Exemples
SET schema_inference_make_columns_nullable = 1;
DESC format(JSONEachRow, $$
{"id" : 1, "age" : 25, "name" : "Josh", "status" : null, "hobbies" : ["football", "cooking"]}
{"id" : 2, "age" : 19, "name" : "Alan", "status" : "married", "hobbies" : ["tennis", "art"]}
$$)
┌─name────┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ id │ Nullable(Int64) │ │ │ │ │ │
│ age │ Nullable(Int64) │ │ │ │ │ │
│ name │ Nullable(String) │ │ │ │ │ │
│ status │ Nullable(String) │ │ │ │ │ │
│ hobbies │ Array(Nullable(String)) │ │ │ │ │ │
└─────────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
SET schema_inference_make_columns_nullable = 'auto';
DESC format(JSONEachRow, $$
{"id" : 1, "age" : 25, "name" : "Josh", "status" : null, "hobbies" : ["football", "cooking"]}
{"id" : 2, "age" : 19, "name" : "Alan", "status" : "married", "hobbies" : ["tennis", "art"]}
$$)
┌─name────┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ id │ Int64 │ │ │ │ │ │
│ age │ Int64 │ │ │ │ │ │
│ name │ String │ │ │ │ │ │
│ status │ Nullable(String) │ │ │ │ │ │
│ hobbies │ Array(String) │ │ │ │ │ │
└─────────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
SET schema_inference_make_columns_nullable = 0;
DESC format(JSONEachRow, $$
{"id" : 1, "age" : 25, "name" : "Josh", "status" : null, "hobbies" : ["football", "cooking"]}
{"id" : 2, "age" : 19, "name" : "Alan", "status" : "married", "hobbies" : ["tennis", "art"]}
$$)
┌─name────┬─type──────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ id │ Int64 │ │ │ │ │ │
│ age │ Int64 │ │ │ │ │ │
│ name │ String │ │ │ │ │ │
│ status │ String │ │ │ │ │ │
│ hobbies │ Array(String) │ │ │ │ │ │
└─────────┴───────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Ce paramètre ne s’applique pas au type de données JSON.
S’il est activé, ClickHouse essaiera d’inférer des entiers plutôt que des nombres à virgule flottante lors de l’inférence du schéma pour les formats texte.
Si tous les nombres de la colonne dans les données d’échantillon sont des entiers, le type résultant sera Int64 ; si au moins un nombre est à virgule flottante, le type résultant sera Float64.
Si les données d’échantillon ne contiennent que des entiers et qu’au moins l’un d’eux est positif et dépasse Int64, ClickHouse inférera UInt64.
Activé par défaut.
Exemples
SET input_format_try_infer_integers = 0
DESC format(JSONEachRow, $$
{"number" : 1}
{"number" : 2}
$$)
┌─name───┬─type──────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ number │ Nullable(Float64) │ │ │ │ │ │
└────────┴───────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
SET input_format_try_infer_integers = 1
DESC format(JSONEachRow, $$
{"number" : 1}
{"number" : 2}
$$)
┌─name───┬─type────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ number │ Nullable(Int64) │ │ │ │ │ │
└────────┴─────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
DESC format(JSONEachRow, $$
{"number" : 1}
{"number" : 18446744073709551615}
$$)
┌─name───┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ number │ Nullable(UInt64) │ │ │ │ │ │
└────────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
DESC format(JSONEachRow, $$
{"number" : 1}
{"number" : 2.2}
$$)
┌─name───┬─type──────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ number │ Nullable(Float64) │ │ │ │ │ │
└────────┴───────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
S’il est activé, ClickHouse essaiera d’inférer le type DateTime ou DateTime64 à partir des champs de type chaîne lors de l’inférence du schéma pour les formats texte.
Si tous les champs d’une colonne dans les données d’exemple ont pu être interprétés avec succès comme des valeurs datetime, le type résultant sera DateTime ou DateTime64(9) (si au moins une valeur datetime comportait une partie fractionnaire),
si au moins un champ n’a pas pu être interprété comme une valeur datetime, le type résultant sera String.
Activé par défaut.
Exemples
SET input_format_try_infer_datetimes = 0;
DESC format(JSONEachRow, $$
{"datetime" : "2021-01-01 00:00:00", "datetime64" : "2021-01-01 00:00:00.000"}
{"datetime" : "2022-01-01 00:00:00", "datetime64" : "2022-01-01 00:00:00.000"}
$$)
┌─name───────┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ datetime │ Nullable(String) │ │ │ │ │ │
│ datetime64 │ Nullable(String) │ │ │ │ │ │
└────────────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
SET input_format_try_infer_datetimes = 1;
DESC format(JSONEachRow, $$
{"datetime" : "2021-01-01 00:00:00", "datetime64" : "2021-01-01 00:00:00.000"}
{"datetime" : "2022-01-01 00:00:00", "datetime64" : "2022-01-01 00:00:00.000"}
$$)
┌─name───────┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ datetime │ Nullable(DateTime) │ │ │ │ │ │
│ datetime64 │ Nullable(DateTime64(9)) │ │ │ │ │ │
└────────────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
DESC format(JSONEachRow, $$
{"datetime" : "2021-01-01 00:00:00", "datetime64" : "2021-01-01 00:00:00.000"}
{"datetime" : "unknown", "datetime64" : "unknown"}
$$)
┌─name───────┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ datetime │ Nullable(String) │ │ │ │ │ │
│ datetime64 │ Nullable(String) │ │ │ │ │ │
└────────────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
S’il est activé, ClickHouse inférera toujours DateTime64(9) lorsque input_format_try_infer_datetimes est activé, même si les valeurs de date et d’heure ne comportent pas de partie fractionnaire.
Désactivé par défaut.
Exemples
SET input_format_try_infer_datetimes = 1;
SET input_format_try_infer_datetimes_only_datetime64 = 1;
DESC format(JSONEachRow, $$
{"datetime" : "2021-01-01 00:00:00", "datetime64" : "2021-01-01 00:00:00.000"}
{"datetime" : "2022-01-01 00:00:00", "datetime64" : "2022-01-01 00:00:00.000"}
$$)
┌─name───────┬─type────────────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ datetime │ Nullable(DateTime64(9)) │ │ │ │ │ │
│ datetime64 │ Nullable(DateTime64(9)) │ │ │ │ │ │
└────────────┴─────────────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Remarque : lors de l’inférence du schéma, l’analyse des valeurs DateTime respecte le paramètre date_time_input_format
S’il est activé, ClickHouse essaiera d’inférer le type Date à partir des champs de chaîne lors de l’inférence du schéma pour les formats texte.
Si tous les champs d’une colonne dans les données d’échantillon ont été correctement interprétés comme des dates, le type résultant sera Date,
si au moins un champ n’a pas pu être interprété comme une date, le type résultant sera String.
Activé par défaut.
Exemples
SET input_format_try_infer_datetimes = 0, input_format_try_infer_dates = 0
DESC format(JSONEachRow, $$
{"date" : "2021-01-01"}
{"date" : "2022-01-01"}
$$)
┌─name─┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ date │ Nullable(String) │ │ │ │ │ │
└──────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
SET input_format_try_infer_dates = 1
DESC format(JSONEachRow, $$
{"date" : "2021-01-01"}
{"date" : "2022-01-01"}
$$)
┌─name─┬─type───────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ date │ Nullable(Date) │ │ │ │ │ │
└──────┴────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
DESC format(JSONEachRow, $$
{"date" : "2021-01-01"}
{"date" : "unknown"}
$$)
┌─name─┬─type─────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ date │ Nullable(String) │ │ │ │ │ │
└──────┴──────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
S’il est activé, ClickHouse essaiera d’inférer les nombres à virgule flottante en notation exponentielle pour les formats texte (sauf JSON, où les nombres en notation exponentielle sont toujours inférés).
Désactivé par défaut.
Exemple
SET input_format_try_infer_exponent_floats = 1;
DESC format(CSV,
$$1.1E10
2.3e-12
42E00
$$)
┌─name─┬─type──────────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ c1 │ Nullable(Float64) │ │ │ │ │ │
└──────┴───────────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Les formats auto-descriptifs contiennent dans les données elles-mêmes des informations sur leur structure :
il peut s’agir d’un en-tête avec une description, d’un arbre binaire de types ou d’une sorte de table.
Pour inférer automatiquement un schéma à partir de fichiers dans de tels formats, ClickHouse lit une partie des données contenant
des informations sur les types et les convertit en schéma de table ClickHouse.
ClickHouse prend en charge certains formats texte avec le suffixe -WithNamesAndTypes. Ce suffixe signifie que les données contiennent deux lignes supplémentaires indiquant les noms et les types de colonnes avant les données proprement dites.
Lors de l’inférence du schéma pour ces formats, ClickHouse lit les deux premières lignes et en extrait les noms et les types de colonnes.
Exemple
DESC format(TSVWithNamesAndTypes,
$$num str arr
UInt8 String Array(UInt8)
42 Hello, World! [1,2,3]
$$)
┌─name─┬─type─────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ num │ UInt8 │ │ │ │ │ │
│ str │ String │ │ │ │ │ │
│ arr │ Array(UInt8) │ │ │ │ │ │
└──────┴──────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Certains formats d’entrée JSON (JSON, JSONCompact, JSONColumnsWithMetadata) contiennent des métadonnées incluant les noms et les types des colonnes.
Lors de l’inférence du schéma pour ces formats, ClickHouse lit ces métadonnées.
Exemple
DESC format(JSON, $$
{
"meta":
[
{
"name": "num",
"type": "UInt8"
},
{
"name": "str",
"type": "String"
},
{
"name": "arr",
"type": "Array(UInt8)"
}
],
"data":
[
{
"num": 42,
"str": "Hello, World",
"arr": [1,2,3]
}
],
"rows": 1,
"statistics":
{
"elapsed": 0.005723915,
"rows_read": 1,
"bytes_read": 1
}
}
$$)
┌─name─┬─type─────────┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ num │ UInt8 │ │ │ │ │ │
│ str │ String │ │ │ │ │ │
│ arr │ Array(UInt8) │ │ │ │ │ │
└──────┴──────────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Avec le format Avro, ClickHouse lit le schéma à partir des données et le convertit en schéma ClickHouse à l’aide des correspondances de types suivantes :
| Type de données Avro | Type de données ClickHouse |
|---|
boolean | Bool |
int | Int32 |
int (date) * | Date32 |
long | Int64 |
float | Float32 |
double | Float64 |
bytes, string | String |
fixed | FixedString(N) |
enum | Enum |
array(T) | Array(T) |
union(null, T), union(T, null) | Nullable(T) |
null | Nullable(Nothing) |
string (uuid) * | UUID |
binary (decimal) * | Decimal(P, S) |
Les autres types Avro ne sont pas pris en charge.
Dans le format Parquet, ClickHouse lit le schéma à partir des données et le convertit en schéma ClickHouse à l’aide des correspondances de types suivantes :
| Type de données Parquet | Type de données ClickHouse |
|---|
BOOL | Bool |
UINT8 | UInt8 |
INT8 | Int8 |
UINT16 | UInt16 |
INT16 | Int16 |
UINT32 | UInt32 |
INT32 | Int32 |
UINT64 | UInt64 |
INT64 | Int64 |
FLOAT | Float32 |
DOUBLE | Float64 |
DATE | Date32 |
TIME (ms) | DateTime |
TIMESTAMP, TIME (us, ns) | DateTime64 |
STRING, BINARY | String |
DECIMAL | Decimal |
LIST | Array |
STRUCT | Tuple |
MAP | Map |
Les autres types Parquet ne sont pas pris en charge.
Au format Arrow, ClickHouse lit le schéma à partir des données et le convertit en schéma ClickHouse grâce aux correspondances de types suivantes :
| Type de données Arrow | Type de données ClickHouse |
|---|
BOOL | Bool |
UINT8 | UInt8 |
INT8 | Int8 |
UINT16 | UInt16 |
INT16 | Int16 |
UINT32 | UInt32 |
INT32 | Int32 |
UINT64 | UInt64 |
INT64 | Int64 |
FLOAT, HALF_FLOAT | Float32 |
DOUBLE | Float64 |
DATE32 | Date32 |
DATE64 | DateTime |
TIMESTAMP, TIME32, TIME64 | DateTime64 |
STRING, BINARY | String |
DECIMAL128, DECIMAL256 | Decimal |
LIST | Array |
STRUCT | Tuple |
MAP | Map |
Les autres types Arrow ne sont pas pris en charge.
Dans le format ORC, ClickHouse lit le schéma à partir des données et le convertit en schéma ClickHouse à l’aide des correspondances de types suivantes :
| Type de données ORC | Type de données ClickHouse |
|---|
Boolean | Bool |
Tinyint | Int8 |
Smallint | Int16 |
Int | Int32 |
Bigint | Int64 |
Float | Float32 |
Double | Float64 |
Date | Date32 |
Timestamp | DateTime64 |
String, Char, Varchar,BINARY | String |
Decimal | Decimal |
List | Array |
Struct | Tuple |
Map | Map |
Les autres types ORC ne sont pas pris en charge.
Le format Native est utilisé en interne dans ClickHouse et intègre le schéma aux données.
Lors de l’inférence de schéma, ClickHouse lit le schéma à partir des données sans aucune transformation.
Ces formats nécessitent un schéma décrivant les données dans un fichier distinct, rédigé dans un langage de schéma spécifique.
Pour inférer automatiquement un schéma à partir de fichiers dans ces formats, ClickHouse lit le schéma externe depuis un fichier distinct et le convertit en schéma de table ClickHouse.
Pour l’inférence de schéma au format Protobuf, ClickHouse utilise les correspondances de types suivantes :
| type de données Protobuf | type de données ClickHouse |
|---|
bool | UInt8 |
float | Float32 |
double | Float64 |
int32, sint32, sfixed32 | Int32 |
int64, sint64, sfixed64 | Int64 |
uint32, fixed32 | UInt32 |
uint64, fixed64 | UInt64 |
string, bytes | String |
enum | Enum |
repeated T | Array(T) |
message, group | Tuple |
Pour l’inférence de schéma du format CapnProto, ClickHouse utilise les correspondances de types suivantes :
| Type de données CapnProto | Type de données ClickHouse |
|---|
Bool | UInt8 |
Int8 | Int8 |
UInt8 | UInt8 |
Int16 | Int16 |
UInt16 | UInt16 |
Int32 | Int32 |
UInt32 | UInt32 |
Int64 | Int64 |
UInt64 | UInt64 |
Float32 | Float32 |
Float64 | Float64 |
Text, Data | String |
enum | Enum |
List | Array |
struct | Tuple |
union(T, Void), union(Void, T) | Nullable(T) |
Dans ces formats, chaque valeur sérialisée contient des informations sur son type (et éventuellement sur son nom), mais aucune information n’est fournie sur l’ensemble de la table.
Lors de l’inférence de schéma pour ces formats, ClickHouse lit les données ligne par ligne (jusqu’à input_format_max_rows_to_read_for_schema_inference lignes ou input_format_max_bytes_to_read_for_schema_inference octets) et extrait
le type (et éventuellement le nom) de chaque valeur à partir des données, puis convertit ces types en types ClickHouse.
Dans le format MsgPack, il n’existe pas de délimiteur entre les lignes. Pour utiliser l’inférence de schéma avec ce format, vous devez spécifier le nombre de colonnes de la table
à l’aide du paramètre input_format_msgpack_number_of_columns. ClickHouse utilise les correspondances de types suivantes :
Type de données MessagePack (INSERT) | Type de données ClickHouse |
|---|
int N, uint N, negative fixint, positive fixint | Int64 |
bool | UInt8 |
fixstr, str 8, str 16, str 32, bin 8, bin 16, bin 32 | String |
float 32 | Float32 |
float 64 | Float64 |
uint 16 | Date |
uint 32 | DateTime |
uint 64 | DateTime64 |
fixarray, array 16, array 32 | Array |
fixmap, map 16, map 32 | Map |
Par défaut, tous les types inférés sont encapsulés dans Nullable, mais ce comportement peut être modifié à l’aide du paramètre schema_inference_make_columns_nullable.
Dans BSONEachRow, chaque ligne de données est présentée sous forme de document BSON. Lors de l’inférence de schéma, ClickHouse lit les documents BSON un par un et en extrait les
valeurs, les noms et les types, puis convertit ces types en types ClickHouse selon les correspondances de types suivantes :
| Type BSON | Type ClickHouse |
|---|
\x08 booléen | Bool |
\x10 int32 | Int32 |
\x12 int64 | Int64 |
\x01 double | Float64 |
\x09 date-heure | DateTime64 |
\x05 binaire avec \x00 comme binary subtype, \x02 chaîne, \x0E symbole, \x0D code JavaScript | String |
\x07 ObjectId, | FixedString(12) |
\x05 binaire avec \x04 comme sous-type uuid, taille = 16 | UUID |
\x04 tableau | Array/Tuple (si les types imbriqués sont différents) |
\x03 document | Named Tuple/Map (avec des clés String) |
Par défaut, tous les types inférés sont dans Nullable, mais ce comportement peut être modifié à l’aide du paramètre schema_inference_make_columns_nullable.
Les données de ces formats ont toujours le même schéma.
Dans ce format, ClickHouse lit toute la ligne des données dans une seule colonne de type String. Le type inféré pour ce format est toujours String et le nom de la colonne est line.
Exemple
DESC format(LineAsString, 'Hello\nworld!')
┌─name─┬─type───┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ line │ String │ │ │ │ │ │
└──────┴────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Dans ce format, ClickHouse lit l’intégralité de l’objet JSON des données dans une seule colonne de type String. Le type inféré pour ce format est toujours String, et le nom de la colonne est json.
Exemple
DESC format(JSONAsString, '{"x" : 42, "y" : "Hello, World!"}')
┌─name─┬─type───┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ json │ String │ │ │ │ │ │
└──────┴────────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Dans ce format, ClickHouse lit l’intégralité de l’objet JSON des données dans une seule colonne de type JSON. Pour ce format, le type inféré est toujours JSON et le nom de la colonne est json.
Exemple
DESC format(JSONAsObject, '{"x" : 42, "y" : "Hello, World!"}');
┌─name─┬─type─┬─default_type─┬─default_expression─┬─comment─┬─codec_expression─┬─ttl_expression─┐
│ json │ JSON │ │ │ │ │ │
└──────┴──────┴──────────────┴────────────────────┴─────────┴──────────────────┴────────────────┘
Modes d’inférence de schéma
L’inférence de schéma à partir de l’ensemble des fichiers de données peut fonctionner selon 2 modes distincts : default et union.
Le mode est contrôlé par le paramètre schema_inference_mode.
En mode par défaut, ClickHouse part du principe que tous les fichiers ont le même schéma et essaie de l’inférer en lisant les fichiers un par un jusqu’à ce qu’il y parvienne.
Exemple :
Supposons que nous ayons 3 fichiers data1.jsonl, data2.jsonl et data3.jsonl avec le contenu suivant :
data1.jsonl:
{"field1" : 1, "field2" : null}
{"field1" : 2, "field2" : null}
{"field1" : 3, "field2" : null}
data2.jsonl :
{"field1" : 4, "field2" : "Data4"}
{"field1" : 5, "field2" : "Data5"}
{"field1" : 6, "field2" : "Data5"}
data3.jsonl:
{"field1" : 7, "field2" : "Data7", "field3" : [1, 2, 3]}
{"field1" : 8, "field2" : "Data8", "field3" : [4, 5, 6]}
{"field1" : 9, "field2" : "Data9", "field3" : [7, 8, 9]}
Essayons d’utiliser l’inférence de schéma sur ces 3 fichiers :
:) DESCRIBE file('data{1,2,3}.jsonl') SETTINGS schema_inference_mode='default'
┌─name───┬─type─────────────┐
│ field1 │ Nullable(Int64) │
│ field2 │ Nullable(String) │
└────────┴──────────────────┘
Comme on peut le constater, nous n’avons pas field3 du fichier data3.jsonl.
Cela s’explique par le fait que ClickHouse a d’abord tenté d’inférer le schéma à partir du fichier data1.jsonl, mais a échoué car le champ field2 ne contenait que des valeurs NULL,
puis a tenté d’inférer le schéma à partir de data2.jsonl et a réussi ; les données du fichier data3.jsonl n’ont donc pas été lues.
En mode union, ClickHouse part du principe que les fichiers peuvent avoir des schémas différents ; il en infère donc les schémas, puis les fusionne en un schéma commun.
Supposons que nous ayons 3 fichiers data1.jsonl, data2.jsonl et data3.jsonl avec le contenu suivant :
data1.jsonl:
{"field1" : 1}
{"field1" : 2}
{"field1" : 3}
data2.jsonl:
{"field2" : "Data4"}
{"field2" : "Data5"}
{"field2" : "Data5"}
data3.jsonl:
{"field3" : [1, 2, 3]}
{"field3" : [4, 5, 6]}
{"field3" : [7, 8, 9]}
Essayons d’appliquer l’inférence de schéma à ces 3 fichiers :
:) DESCRIBE file('data{1,2,3}.jsonl') SETTINGS schema_inference_mode='union'
┌─name───┬─type───────────────────┐
│ field1 │ Nullable(Int64) │
│ field2 │ Nullable(String) │
│ field3 │ Array(Nullable(Int64)) │
└────────┴────────────────────────┘
Comme on peut le voir, nous avons tous les champs de tous les fichiers.
Remarque :
- Comme certains fichiers peuvent ne pas contenir certaines colonnes du schéma résultant, le mode union est pris en charge uniquement pour les formats qui permettent la lecture d’un sous-ensemble de colonnes (comme JSONEachRow, Parquet, TSVWithNames, etc.) et ne fonctionnera pas avec les autres formats (comme CSV, TSV, JSONCompactEachRow, etc.).
- Si ClickHouse ne parvient pas à inférer le schéma de l’un des fichiers, une exception sera levée.
- Si vous avez beaucoup de fichiers, la lecture du schéma de chacun d’eux peut prendre beaucoup de temps.
Si le format des données n’est pas indiqué et ne peut pas être déterminé à partir de l’extension du fichier, ClickHouse essaiera de détecter le format du fichier à partir de son contenu.
Exemples :
Supposons que data contienne les éléments suivants :
"a","b"
1,"Data1"
2,"Data2"
3,"Data3"
Nous pouvons examiner et interroger ce fichier sans préciser le format ni la structure :
┌─name─┬─type─────────────┐
│ a │ Nullable(Int64) │
│ b │ Nullable(String) │
└──────┴──────────────────┘
:) select * from file(data);
┌─a─┬─b─────┐
│ 1 │ Data1 │
│ 2 │ Data2 │
│ 3 │ Data3 │
└───┴───────┘
ClickHouse ne peut détecter qu’un nombre limité de formats, et cette détection prend un certain temps. Il est donc toujours préférable de spécifier explicitement le format.