Skip to main content

Description

Le format RowBinary analyse les données ligne par ligne, au format binaire. Les lignes et les valeurs se succèdent, sans séparateurs. Comme les données sont au format binaire, le délimiteur après FORMAT RowBinary est strictement défini comme suit :
  • N’importe quel nombre de caractères d’espacement :
    • ' ' (espace - code 0x20)
    • '\t' (tabulation - code 0x09)
    • '\f' (saut de page - code 0x0C)
  • Suivi d’exactement une séquence de retour à la ligne :
    • style Windows "\r\n"
    • ou style Unix '\n'
  • Immédiatement suivi de données binaires.
Ce format est moins efficace que le format Native, car il est basé sur les lignes.

Format binaire des types de données

La plupart des requêtes fournies dans les exemples peuvent être exécutées avec curl en redirigeant la sortie vers un fichier.
Les données peuvent ensuite être examinées avec un éditeur hexadécimal.

LEB128 non signé (Little Endian Base 128)

Un encodage d’entier non signé à largeur variable en little-endian, utilisé pour représenter la longueur de types de données de taille variable tels que String, Array et Map. Vous trouverez un exemple d’implémentation sur la page wiki de LEB128.

(U)Int8, (U)Int16, (U)Int32, (U)Int64, (U)Int128, (U)Int256

Tous les types entiers sont encodés sur un nombre d’octets approprié en little-endian. Les types signés (Int8 à Int256) utilisent la représentation en complément à deux. La plupart des langages permettent d’extraire ce type d’entiers à partir de tableaux d’octets, à l’aide d’outils intégrés ou de bibliothèques bien connues. Pour Int128/Int256 et UInt128/UInt256, qui dépassent la taille des entiers prise en charge nativement par la plupart des langages, une désérialisation personnalisée peut être nécessaire.

Bool

Les valeurs booléennes sont encodées sur un seul octet et peuvent être désérialisées comme UInt8.
  • 0 correspond à false
  • 1 correspond à true

Float32, Float64

Little-endian : nombres à virgule flottante codés sur 4 octets pour Float32 et 8 octets pour Float64. Comme pour les entiers, la plupart des langages fournissent les outils appropriés pour désérialiser ces valeurs.

BFloat16

BFloat16 (Brain Floating Point) est un format à virgule flottante sur 16 bits qui offre la plage de Float32 avec une précision réduite, ce qui le rend utile pour les charges de travail de machine learning. Le format de transmission correspond essentiellement aux 16 bits de poids fort d’une valeur Float32. Si votre langage ne le prend pas en charge nativement, le moyen le plus simple de le gérer consiste à lire et écrire des valeurs en UInt16, puis à les convertir depuis et vers Float32 : Pour convertir BFloat16 en Float32 (pseudocode) :
Pour convertir Float32 en BFloat16 (pseudo-code) :
Exemples de valeurs sous-jacentes pour BFloat16 :

Decimal32, Decimal64, Decimal128, Decimal256

Les types Decimal sont représentés sous forme d’entiers little-endian de la largeur en bits correspondante.
  • Decimal32 - 4 octets, ou Int32.
  • Decimal64 - 8 octets, ou Int64.
  • Decimal128 - 16 octets, ou Int128.
  • Decimal256 - 32 octets, ou Int256.
Lors de la désérialisation d’une valeur Decimal, les parties entière et fractionnaire peuvent être obtenues à l’aide du pseudocode suivant :
où trunc effectue une troncature vers zéro (et non une division avec arrondi à l’inférieur, qui diffère pour les valeurs négatives), et scale est le nombre de chiffres après la virgule. Par exemple, pour Decimal(10, 2) (l’équivalent de Decimal32(2)), l’échelle est 2, et la valeur 12345 sera représentée sous la forme (123, 45). La sérialisation nécessite l’opération inverse :
Pour plus de détails, consultez la documentation ClickHouse sur les types Decimal.

String

Les chaînes ClickHouse sont des séquences arbitraires d’octets. Il n’est pas nécessaire qu’elles soient en UTF-8 valide. Le préfixe de longueur correspond à la longueur en octets, et non au nombre de caractères. Encodage en deux parties :
  1. Un entier de longueur variable (LEB128) qui indique la longueur de la chaîne en octets.
  2. Les octets bruts de la chaîne.
Par exemple, une chaîne foobar sera encodée sur sept octets, comme suit :

FixedString

Contrairement à String, FixedString a une longueur fixe, définie dans le schéma. Il est encodé sous forme d’une séquence d’octets, complétée par des octets nuls en fin de chaîne si la valeur est plus courte que N.
Lors de la lecture d’une FixedString, les octets nuls de fin peuvent correspondre soit au remplissage, soit à de véritables caractères \0 dans les données ; il est impossible de les distinguer dans la représentation binaire transmise. ClickHouse lui-même conserve les N octets tels quels.
Une FixedString(3) vide ne contient que des octets nuls de remplissage :
FixedString(3) non vide contenant la chaîne de caractères hi :
FixedString(3) non vide contenant la chaîne bar :
Aucun remplissage n’est nécessaire dans le dernier exemple, car les trois octets sont utilisés.

Date

Stocké sous forme de UInt16 (deux octets), représentant le nombre de jours depuis le 1970-01-01. Plage de valeurs prise en charge : [1970-01-01, 2149-06-06]. Exemples de valeurs sous-jacentes pour Date :

Date32

Stocké au format Int32 (quatre octets) représentant le nombre de jours avant ou après 1970-01-01. Plage de valeurs prise en charge : [0000-01-01, 9999-12-31]. Exemples de valeurs sous-jacentes pour Date32 :
Une date avant l’epoch :

DateTime

Stocké au format UInt32 (quatre octets), représentant le nombre de secondes depuis 1970-01-01 00:00:00 UTC. Syntaxe :
Par exemple, DateTime ou DateTime('UTC').
La valeur binaire est toujours un décalage par rapport à l’epoch UTC. Le fuseau horaire ne modifie pas l’encodage. En revanche, le fuseau horaire a bien une incidence sur la façon dont les chaînes de caractères sont interprétées lors de l’insertion : l’insertion de '2024-01-15 10:30:00' dans une colonne DateTime('America/New_York') stocke une valeur d’epoch différente de celle obtenue en insérant la même chaîne dans une colonne DateTime('UTC'), car la chaîne est interprétée comme une heure locale dans le fuseau horaire de la colonne. Sur le wire, dans les deux cas, il s’agit simplement de secondes d’epoch UInt32.
Plage de valeurs prise en charge : [1970-01-01 00:00:00, 2106-02-07 06:28:15]. Exemples de valeurs sous-jacentes pour DateTime :

DateTime64

Stocké sous la forme d’un Int64 (huit octets), représentant le nombre de ticks avant ou après 1970-01-01 00:00:00 UTC. La résolution des ticks est définie par le paramètre precision ; voir la syntaxe ci-dessous :
Où precision est un entier compris entre 0 et 9. En général, seules les valeurs suivantes sont utilisées : 3 (millisecondes), 6 (microsecondes), 9 (nanosecondes). Exemples de définitions DateTime64 valides : DateTime64(0), DateTime64(3), DateTime64(6, 'UTC') ou DateTime64(9, 'Europe/Amsterdam').
Comme pour DateTime, la valeur binaire est toujours un décalage par rapport à l’epoch UTC. Le fuseau horaire affecte la façon dont les chaînes sont interprétées lors de l’insertion (voir la note DateTime), mais l’encodage lui-même correspond toujours à des ticks Int64 depuis l’epoch UTC.
La valeur Int64 sous-jacente du type DateTime64 peut être interprétée comme le nombre d’unités suivantes avant ou après l’epoch UNIX :
  • DateTime64(0) - secondes.
  • DateTime64(3) - millisecondes.
  • DateTime64(6) - microsecondes.
  • DateTime64(9) - nanosecondes.
Plage de valeurs prise en charge : [0000-01-01 00:00:00, 9999-12-31 23:59:59.999999999] (pour une précision jusqu’à 7 ; les précisions 8 et 9 sont plus limitées, voir la note ci-dessous). Exemples de valeurs sous-jacentes pour DateTime64 :
  • DateTime64(3) : la valeur 1546300800000 représente 2019-01-01 00:00:00 UTC.
  • DateTime64(6) : la valeur 1705314600123456 représente 2024-01-15 10:30:00.123456 UTC.
  • DateTime64(9) : la valeur 1705314600123456789 représente 2024-01-15 10:30:00.123456789 UTC.
Comme la plage sous-jacente des ticks Int64 est plus limitée à des précisions plus élevées, la valeur maximale prise en charge diminue : avec une précision de 8, elle est de 4892-10-07 et avec une précision de 9 (nanosecondes), elle est de 2262-04-11 23:47:16 UTC.

Time

Stocké au format Int32, représentant une valeur temporelle en secondes. Les valeurs négatives sont valides. Plage de valeurs prise en charge : [-999:59:59, 999:59:59] (c.-à-d. [-3599999, 3599999] secondes).
À l’heure actuelle, le paramètre enable_time_time64_type doit être défini sur 1 pour utiliser Time ou Time64.
Exemples de valeurs sous-jacentes pour Time :

Time64

Stocké en interne sous forme de Decimal64 (lui-même stocké sous forme de Int64), il représente une valeur d’heure avec des fractions de seconde, avec une précision configurable. Les valeurs négatives sont autorisées. Syntaxe :
Où precision est un entier compris entre 0 et 9. Valeurs courantes : 3 (millisecondes), 6 (microsecondes), 9 (nanosecondes). Plage de valeurs prise en charge : [-999:59:59.xxxxxxxxx, 999:59:59.xxxxxxxxx].
À l’heure actuelle, le paramètre enable_time_time64_type doit être défini sur 1 pour utiliser Time ou Time64.
La valeur Int64 sous-jacente représente des fractions de seconde mises à l’échelle par 10^precision. Exemples de valeurs sous-jacentes pour Time64 :

Types d’intervalle

Tous les types d’intervalle sont stockés sous forme de Int64 (huit octets, little-endian). La valeur représente le nombre d’unités de temps correspondantes. Les valeurs négatives sont valides. Les types d’intervalle sont : IntervalNanosecond, IntervalMicrosecond, IntervalMillisecond, IntervalSecond, IntervalMinute, IntervalHour, IntervalDay, IntervalWeek, IntervalMonth, IntervalQuarter, IntervalYear.
Le nom du type d’intervalle (par ex., IntervalSecond vs IntervalDay) détermine l’unité de la valeur stockée. L’encodage binaire est toujours le même.
Exemples de valeurs sous-jacentes :

Enum8, Enum16

Stocké sous la forme d’un seul octet (Enum8 == Int8) ou de deux octets (Enum16 == Int16) représentant l’index de la valeur enum dans la définition de l’enum. Notez que le type de stockage est signé — les valeurs enum peuvent être négatives (par ex., Enum8('a' = -128, 'b' = 0)). Un Enum peut être défini simplement, comme ceci :
L’Enum8 défini ci-dessus correspondra aux valeurs suivantes côté client :
Ou, de manière plus complexe, comme ceci :
L’Enum16 défini ci-dessus correspondra aux valeurs suivantes côté client :
Pour l’analyseur de types de données, la principale difficulté consiste à repérer les caractères d’échappement dans la définition de l’enum, tels que \', ainsi que les caractères spéciaux comme = qui peuvent apparaître à l’intérieur de chaînes entre guillemets.

UUID

Représenté sous la forme d’une séquence de 16 octets. L’UUID est stocké sous la forme de deux valeurs UInt64 little-endian : les 8 premiers octets de la représentation UUID standard sont inversés octet par octet, et les 8 octets suivants le sont indépendamment. Par exemple, pour l’UUID 61f0c404-5cb3-11e7-907b-a6006ad3dba0 :
  • Représentation standard en octets : 61 f0 c4 04 5c b3 11 e7 | 90 7b a6 00 6a d3 db a0
  • Première moitié inversée (LE UInt64) : e7 11 b3 5c 04 c4 f0 61
  • Seconde moitié inversée (LE UInt64) : a0 db d3 6a 00 a6 7b 90
Exemples de valeurs sous-jacentes pour UUID :
  • 61f0c404-5cb3-11e7-907b-a6006ad3dba0 est représenté comme suit :
  • L’UUID par défaut 00000000-0000-0000-0000-000000000000 est représenté sous la forme de 16 octets nuls :
Il peut être utilisé lorsqu’un nouvel enregistrement a été inséré sans que la valeur UUID ait été spécifiée.

IPv4

Stocké sur quatre octets sous la forme de UInt32 en ordre des octets little-endian. Notez que cela diffère de l’ordre traditionnel des octets réseau (big-endian) couramment utilisé pour les adresses IP. Exemples de valeurs sous-jacentes pour IPv4 :

IPv6

Stockée sur 16 octets, en big-endian / ordre des octets réseau (octet de poids fort en premier). Exemples de valeurs sous-jacentes pour IPv6 :

Nullable

Un type de données nullable est encodé comme suit :
  1. Un seul octet indique si la valeur est NULL ou non :
    • 0x00 signifie que la valeur n’est pas NULL.
    • 0x01 signifie que la valeur est NULL.
  2. Si la valeur n’est pas NULL, le type de données sous-jacent est encodé normalement. Si la valeur est NULL, aucun octet supplémentaire n’est écrit pour le type sous-jacent.
Par exemple, une valeur Nullable(UInt32) :

LowCardinality

Dans le format RowBinary, le marqueur de faible cardinalité n’affecte pas le format binaire. Par exemple, LowCardinality(String) est encodé de la même manière qu’un String classique.
Cela s’applique uniquement à RowBinary. Dans le format Native, LowCardinality utilise un encodage différent, basé sur un dictionnaire.
Une colonne peut être définie comme LowCardinality(Nullable(T)), mais il n’est pas possible de la définir comme Nullable(LowCardinality(T)) - cela entraînera toujours une erreur du serveur.
Lors des tests, allow_suspicious_low_cardinality_types peut être défini sur 1 pour autoriser la plupart des types de données dans LowCardinality afin d’améliorer la couverture.

Array

Un tableau est encodé de la manière suivante :
  1. Un entier à longueur variable (LEB128) indiquant le nombre d’éléments du tableau.
  2. Les éléments du tableau, encodés de la même manière que le type de données sous-jacent.
Par exemple, un tableau contenant des valeurs UInt32 :
Un exemple légèrement plus complexe :
Un tableau peut contenir des valeurs de type Nullable, mais le tableau lui-même ne peut pas être de type Nullable.
Ce qui suit est valide :
Et il sera encodé comme suit :
Vous trouverez un exemple de traitement des tableaux multidimensionnels dans la section Geo.

Tuple

Un tuple est encodé avec tous ses éléments placés les uns à la suite des autres dans leur format binaire respectif, sans métadonnées ni délimiteurs supplémentaires.
L’encodage sous forme de chaîne du type de données Tuple pose des difficultés similaires à celles du type Enum, notamment pour le suivi des symboles d’échappement et des caractères spéciaux ; avec Tuple, il faut en plus tenir compte des parenthèses ouvrantes et fermantes. Notez également que les Tuples les plus complexes peuvent contenir d’autres Tuples imbriqués, des Arrays, des Maps et même des enums. Par exemple, dans le tableau suivant, le tuple contient un enum dont le nom comprend un backtick et une parenthèse, ce qui peut entraîner des problèmes d’analyse s’il n’est pas correctement traité :

Map

Une map peut être vue comme un Array(Tuple(K, V)), où K est le type de clé et V le type de valeur. La map est encodée comme suit :
  1. Un entier de longueur variable (LEB128) indiquant le nombre d’éléments de la map.
  2. Les éléments de la map sous forme de paires clé-valeur, encodés selon leurs types correspondants.
Par exemple, une map avec des clés String et des valeurs UInt32 :
Il est possible d’avoir des valeurs de type Map avec des structures profondément imbriquées, telles que Map(String, Map(Int32, Array(Nullable(String)))), qui seront encodées de façon similaire à celle décrite ci-dessus.

Variant

Ce type représente une union d’autres types de données. Le type Variant(T1, T2, ..., TN) signifie que chaque ligne de ce type possède une valeur de type T1, T2, …, TN ou aucune de ces valeurs (valeur NULL).
Bien que, pour l’utilisateur final, Variant(T1, T2) signifie exactement la même chose que Variant(T2, T1), l’ordre des types dans la définition est important pour le format binaire : les types de la définition sont toujours triés par ordre alphabétique, et c’est important, car la variante exacte est encodée par un “discriminant” - l’indice du type de données dans la définition.
Prenons l’exemple suivant :
Une valeur NULL est codée avec un octet de discriminant de 0xFF :
Le paramètre allow_suspicious_variant_types peut être utilisé pour autoriser des tests plus exhaustifs du type Variant.

Dynamic

Le type Dynamic peut contenir des valeurs de n’importe quel type, dont le type est déterminé à l’exécution. Dans le format RowBinary, chaque valeur est auto-descriptive : la première partie correspond à la spécification du type dans ce format. Le contenu suit ensuite, avec l’encodage de la valeur tel qu’il est décrit dans ce document. Pour analyser une valeur, il suffit donc d’utiliser l’index de type pour déterminer le bon parseur, puis de réutiliser l’analyse RowBinary déjà implémentée ailleurs.
BinaryTypeIndex est un octet unique qui identifie le type. Consultez la référence ici pour connaître les indices de type et les paramètres. Une valeur Dynamic NULL est encodée avec BinaryTypeIndex 0x00 (le type Nothing), sans octet supplémentaire :
Exemples :

JSON

Le type JSON encode les données en deux catégories distinctes :
  1. Chemins typés - Chemins déclarés avec des types explicites dans le schéma (par ex. JSON(user_id UInt32, name String))
  2. Chemins dynamiques/Chemins de débordement lorsque la limite de chemins dynamiques est dépassée - Chemins détectés à l’exécution et stockés avec le type Dynamic. L’encodage de la valeur est précédé de la définition du type.
Le wire format et les règles sont différents pour ces deux catégories. Les chemins sont sérialisés en trois groupes, écrits de manière séquentielle : les chemins typés, les chemins dynamiques, puis les chemins de données partagées (overflow). Les chemins typés et dynamiques sont écrits dans un ordre implementation-defined (déterminé par l’itération interne de la table de hachage), tandis que les chemins de données partagées sont écrits par ordre alphabétique. Les lecteurs ne doivent pas supposer un ordre particulier des chemins. Le désérialiseur distribue chaque chemin par son nom, et non par sa position. Chaque ligne JSON au format RowBinary est sérialisée de la manière suivante :
Exemples : 1. JSON simple avec chemins typés uniquement : Schéma : JSON(user_id UInt32, active Bool) Ligne : {"user_id": 42, "active": true} Encodage binaire (hexadécimal avec annotations) :
2. JSON simple avec chemins typés et chemins dynamiques : Schéma : JSON(user_id UInt32, active Bool) Ligne : {"user_id": 42, "active": true, "name": "Alice"} Encodage binaire (hexadécimal avec annotations) :
3. Gestion des valeurs NULL : Avec une colonne Nullable typée, vous obtenez null : Schéma : JSON(score Nullable(Int32)) Row: {"score": null } Encodage binaire (hexadécimal avec annotations) :
Avec une colonne typée non nullable, vous obtenez la valeur par défaut : Schéma : JSON(name String) Ligne : {"name": null} Encodage binaire :
Avec un chemin dynamique, il est ignoré : Schema: JSON(id UInt64) Ligne : {"id": 100, "metadata": null} Encodage binaire :
Note : Le chemin metadata avec une valeur NULL n’est pas inclus car les chemins dynamiques ne sont sérialisés que lorsqu’ils sont non-NULL. C’est une différence clé par rapport aux chemins typés. 4. Objets JSON imbriqués : Schéma : JSON() Ligne : {"user": {"name": "Bob", "age": 30}} Encodage binaire (hexadécimal avec annotations) :
Remarque : les objets imbriqués sont aplatis en chemins séparés par des points (par ex. user.name au lieu d’une structure imbriquée). Alternative : JSON en mode String Avec le paramètre output_format_binary_write_json_as_string=1, les colonnes JSON sont sérialisées en une seule chaîne de texte JSON au lieu d’un format binaire structuré. Il existe un paramètre correspondant pour écrire dans des colonnes JSON, input_format_binary_read_json_as_string. Le choix du paramètre dépend ici de l’endroit où vous souhaitez analyser le JSON : côté client ou côté serveur.

Types Geo

Geo est une catégorie de types de données qui représentent des données géographiques. Elle comprend :
  • Point - sous la forme de Tuple(Float64, Float64).
  • MultiPoint - sous la forme de Array(Point) ou Array(Tuple(Float64, Float64)).
  • Ring - sous la forme de Array(Point) ou Array(Tuple(Float64, Float64)).
  • Polygon - sous la forme de Array(Ring) ou Array(Array(Tuple(Float64, Float64))).
  • MultiPolygon - sous la forme de Array(Polygon) ou Array(Array(Array(Tuple(Float64, Float64)))).
  • LineString - sous la forme de Array(Point) ou Array(Tuple(Float64, Float64)).
  • MultiLineString - sous la forme de Array(LineString) ou Array(Array(Tuple(Float64, Float64))).
Le format binaire des valeurs Geo est exactement le même que celui de Tuple et Array. Les en-têtes du format RowBinaryWithNamesAndTypes contiendront les alias de ces types, par exemple Point, MultiPoint, Ring, Polygon, MultiPolygon, LineString et MultiLineString.

Geometry

Geometry est un type Variant capable de contenir n’importe lequel des types Geo listés ci-dessus. Dans le format binaire, il est encodé exactement comme un Variant, avec un octet de discriminant indiquant quel type Geo suit. Les indices du discriminant pour Geometry sont : Structure du format binaire :
Exemple d’encodage d’un Point sous forme de Geometry :
Exemple d’encodage d’un Ring sous forme de Geometry :

Nested

Le format binaire de Nested dépend du paramètre flatten_nested.
Tous les tableaux de composants d’une même ligne doivent avoir la même longueur. Cette contrainte est imposée par le serveur. Si les longueurs ne correspondent pas, des erreurs d’insertion se produiront.

flatten_nested = 1 (par défaut)

Avec le paramètre par défaut, Nested est aplati sous forme de tableaux indépendants. Chaque sous-colonne devient une colonne Array distincte avec un nom séparé par des points :
DESCRIBE TABLE foo affiche les colonnes aplaties :
Chaque type Array est sérialisé indépendamment, comme décrit dans la section Array :

flatten_nested = 0

Avec flatten_nested = 0, Nested est conservé sous la forme d’une seule colonne de type Array(Tuple(...)). Le nom de la colonne n’est pas séparé par des points :
DESCRIBE TABLE foo affiche une seule colonne :
L’encodage est Array(Tuple(String, Int32)) : un préfixe indiquant la longueur du tableau, puis les champs du tuple de chaque élément dans l’ordre :
Notez que les champs sont entrelacés par élément (a₁, b₁, a₂, b₂) plutôt que regroupés par colonne (a₁, a₂, b₁, b₂), comme dans la représentation aplatie.

SimpleAggregateFunction

SimpleAggregateFunction(func, T) est encodé de façon identique à son type de données sous-jacent T. Le nom de la fonction d’agrégation n’a aucun effet sur le format binaire. Par exemple, SimpleAggregateFunction(max, UInt32) est encodé de la même manière qu’un simple UInt32 :
L’en-tête RowBinaryWithNamesAndTypes indique le type SimpleAggregateFunction(max, UInt32), mais dans la représentation binaire, la valeur n’est qu’un UInt32 :

AggregateFunction

AggregateFunction(func, T) stocke l’état intermédiaire complet d’une fonction d’agrégation. Contrairement à SimpleAggregateFunction, qui stocke elle aussi un état intermédiaire mais l’encode de façon identique au type de données sous-jacent, AggregateFunction stocke un blob binaire opaque dont le format est propre à chaque fonction d’agrégation.
Les états d’agrégation n’ont aucun préfixe de longueur dans RowBinary. Un parseur doit comprendre le format de sérialisation interne de chaque fonction d’agrégation pour savoir combien d’octets lire. En pratique, la plupart des clients traitent les états d’agrégation comme opaques et utilisent les combinateurs *State / *Merge afin de laisser le serveur gérer la sérialisation.
Le format interne varie selon la fonction. Voici quelques exemples simples : countState — stocke le nombre sous forme de VarUInt (LEB128) :
sumState — stocke la somme accumulée dans un entier de taille fixe. La taille dépend du type de l’argument (UInt64 pour les arguments entiers) :
minState / maxState — stocke un octet de drapeau suivi de la valeur dans le type sous-jacent. Le drapeau vaut 0x00 pour un état vide (aucune valeur rencontrée) ou 0x01 lorsqu’une valeur est présente :
État vide (aucune ligne agrégée) :
Les fonctions plus complexes comme uniq, quantile ou groupArray utilisent des formats propres à leur implémentation. Si vous devez lire ou écrire ces états, consultez le code source de ClickHouse pour la fonction concernée.
Lorsque le paramètre aggregate_function_input_format vaut value ou array, une colonne AggregateFunction(func, T) est lue comme une valeur de type T (ou un Tuple des types des arguments s’il y en a plusieurs), ou comme un Array de telles valeurs, encodée exactement comme ce type l’est en RowBinary. L’état est construit à partir des valeurs côté serveur. Par exemple, avec aggregate_function_input_format = 'array', une cellule de AggregateFunction(avg, UInt32) est un Array(UInt32) : une longueur LEB128 suivie des éléments.

QBit

QBit est un type de vecteur permettant une recherche efficace avec différents niveaux de précision. En interne, il est stocké dans un format transposé. Dans le format binaire, QBit est simplement un Array du type d’élément sous-jacent (Int8, Float32, Float64 ou BFloat16). L’optimisation de transposition des bits pour le stockage s’effectue côté serveur, et non dans le protocole RowBinary. Syntaxe :
Où element_type est Int8, Float32, Float64 ou BFloat16, et dimension est la dimension fixe du vecteur. Le paramètre stride, facultatif, contrôle uniquement la façon dont les plans de bits sont regroupés dans les flux de stockage côté serveur ; il n’affecte pas le format binaire RowBinary, qui correspond toujours au tableau complet de dimension éléments. Format binaire : identique à Array(element_type) :
Exemple d’encodage de QBit(Float32, 4) contenant [1.0, 2.0, 3.0, 4.0] :

Paramètres du format

Les paramètres suivants sont communs à tous les formats de type RowBinary.
Dernière modification le 26 septembre 2026