Insertion de données avec ClickHouse Connect : utilisation avancée
InsertContexts
ClickHouse Connect exécute les insertions au format Native, c’est-à-dire les méthodes insert et insert_df, dans un InsertContext. Les méthodes insert_arrow, insert_df_arrow et raw_insert envoient directement leurs payloads et n’en utilisent pas. L’InsertContext inclut toutes les valeurs transmises comme arguments à la méthode client insert. De plus, lors de la création initiale d’un InsertContext, ClickHouse Connect récupère les types de données des colonnes à insérer, nécessaires à des insertions efficaces au format Native. En réutilisant l’InsertContext pour plusieurs insertions, cette « pré-requête » est évitée, ce qui rend les insertions plus rapides et plus efficaces.
Il est possible d’obtenir un InsertContext à l’aide de la méthode client create_insert_context. Cette méthode prend les mêmes arguments que la fonction insert, à l’exception de context lui-même. Notez que seule la propriété data des InsertContext doit être modifiée en vue d’une réutilisation. Cela correspond à son objectif : fournir un objet réutilisable pour des insertions répétées de nouvelles données dans la même table.
test_data = [[13, "v1", "v2"], [79, "v3", "v4"]]
ic = client.create_insert_context(table="test_table", data=test_data)
client.insert(context=ic)
assert client.command("SELECT count() FROM test_table") == 2
new_data = [[101, "v5", "v6"], [113, "v7", "v8"]]
ic.data = new_data
client.insert(context=ic)
qr = client.query("SELECT * FROM test_table ORDER BY key DESC")
assert qr.row_count == 4
assert qr.first_row[0] == 113InsertContexts incluent un état mutable mis à jour pendant le processus d’insertion ; ils ne sont donc pas thread-safe.
Formats d'écriture
Les formats d'écriture sont implémentés pour un nombre limité de types. Dans la plupart des cas, ClickHouse Connect détermine automatiquement le format d'écriture approprié pour une colonne à partir de sa première valeur de données non nulle. Par exemple, lorsque la première valeur d'une colonne DateTime est un entier, le client la traite comme un nombre de secondes depuis l'époque Unix.
Il n'est généralement pas nécessaire de remplacer un format d'écriture, mais les méthodes de clickhouse_connect.datatypes.format permettent d'en définir un globalement. Les wrappers de conteneur tels que Array, Nullable et LowCardinality conservent le comportement de mise en forme du type d'élément.
Options de format d'écriture
| Type ClickHouse | Type Python natif | Formats d'écriture | Commentaires |
|---|---|---|---|
| Int[8-64], UInt[8-32] | int | ||
| UInt64 | int | ||
| [U]Int[128,256] | int | ||
| BFloat16 | float | ||
| Float32 | float | ||
| Float64 | float | ||
| Decimal | decimal.Decimal | ||
| String | str or bytes | Une colonne doit contenir de manière cohérente soit du texte, soit des octets. | |
| FixedString | bytes | string | Les valeurs de chaîne sont complétées par des octets nuls. Les octets vides sont écrits comme des octets tous nuls. |
| Enum[8,16] | str or int | Insérez les libellés sous forme de chaînes ou leurs valeurs entières sous-jacentes. | |
| Date | datetime.date | int | Les valeurs entières sont interprétées comme des jours depuis 1970-01-01. |
| Date32 | datetime.date | int | Les valeurs entières sont interprétées comme des décalages signés en jours. |
| DateTime | datetime.datetime | int | Les valeurs entières sont interprétées comme des secondes depuis l'époque Unix. |
| DateTime64 | datetime.datetime | int | Les valeurs entières sont interprétées comme des ticks selon la précision de la colonne. |
| Time | datetime.timedelta | int, string, time | Les valeurs entières sont interprétées comme des secondes. |
| Time64 | datetime.timedelta | int, string, time | Les valeurs entières sont interprétées comme des ticks selon la précision de la colonne. |
| IPv4 | ipaddress.IPv4Address |
string | Des chaînes au format correct peuvent être insérées comme adresses IPv4 |
| IPv6 | ipaddress.IPv6Address |
string | Des chaînes au format correct peuvent être insérées comme adresses IPv6 |
| Tuple | dict or tuple | ||
| Map | dict | ||
| Nested | Sequence[dict] | ||
| UUID | uuid.UUID | string | Des chaînes au format correct peuvent être insérées comme UUID ClickHouse |
| JSON | dict | string | Les dictionnaires et les chaînes contenant un objet JSON sont pris en charge. Le type legacy Object('json') n'est pas pris en charge. |
| Variant | object | Les valeurs utilisent la sérialisation native du type membre. Utilisez clickhouse_connect.datatypes.dynamic.typed_variant lorsque les types Python sont ambigus. |
|
| Dynamic | object | Les valeurs sont actuellement insérées à partir de leur représentation sous forme de chaîne. | |
| QBit | Sequence[float] | NumPy est utilisé automatiquement pour une transposition des bits plus rapide lorsqu’il est installé. |
Méthodes d’insertion spécialisées
ClickHouse Connect fournit des méthodes d’insertion spécialisées pour les formats de données courants :
insert_df– Insère un Pandas DataFrame comme données Native orientées colonnes. Il prend également en charge des noms/types de colonnes explicites ou unInsertContextréutilisable.insert_arrow– Insère une PyArrow Table à l’aide du format d’entrée Arrow de ClickHouse.insert_df_arrow– Insère un Pandas DataFrame adossé à Arrow ou un Polars DataFrame. Les colonnes Pandas doivent toutes utiliser des Dtype adossés à Arrow.
Les trois méthodes acceptent database, settings et les transport_settings HTTP par requête.
Insertion de DataFrame Pandas
import clickhouse_connect
import pandas as pd
client = clickhouse_connect.get_client()
df = pd.DataFrame({
"id": [13, 79],
"name": ["user_1", "user_2"],
"age": [25, 30],
})
client.insert_df("users", df)Insertion d’une table PyArrow
import clickhouse_connect
import pyarrow as pa
client = clickhouse_connect.get_client()
arrow_table = pa.table({
"id": [13, 79],
"name": ["user_1", "user_2"],
"age": [25, 30],
})
client.insert_arrow("users", arrow_table)Insertion d’un DataFrame adossé à Arrow (pandas 2.x)
import clickhouse_connect
import pandas as pd
client = clickhouse_connect.get_client()
# Convert to Arrow-backed dtypes for better performance
df = pd.DataFrame({
"id": [13, 79],
"name": ["user_1", "user_2"],
"age": [25, 30],
}).convert_dtypes(dtype_backend="pyarrow")
client.insert_df_arrow("users", df)Créer une table à partir d’un schéma PyArrow
create_table_from_arrow_schema génère une instruction CREATE TABLE à partir de champs scalaires Arrow courants. La correspondance couvre les entiers signés et non signés, les valeurs à virgule flottante, les booléens, les chaînes, les dates et les horodatages. Elle crée intentionnellement des colonnes ClickHouse non Nullable et lève une exception TypeError pour les types Arrow non pris en charge. Passez en revue le DDL généré avant de l’exécuter.
import clickhouse_connect
import pyarrow as pa
from clickhouse_connect.driver.ddl import create_table_from_arrow_schema
client = clickhouse_connect.get_client()
schema = pa.schema(
[
("id", pa.uint32()),
("name", pa.string()),
("event_time", pa.timestamp("ms", tz="UTC")),
]
)
ddl = create_table_from_arrow_schema(
table_name="arrow_events",
schema=schema,
engine="MergeTree",
engine_params={"ORDER BY": "id"},
)
client.command(ddl)Fuseaux horaires
Lors de l’insertion d’objets Python datetime dans des colonnes DateTime ou DateTime64, ClickHouse Connect les convertit en valeurs d’époque Unix.
Objets datetime avec informations de fuseau horaire
Les objets avec informations de fuseau horaire préservent l’instant représenté. Il n’est pas nécessaire que le fuseau horaire source corresponde à celui déclaré sur la colonne ClickHouse.
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
client.command("CREATE TABLE events (event_time DateTime) ENGINE Memory")
data = [
[datetime(2023, 6, 15, 10, 30, tzinfo=timezone.utc)],
[datetime(2023, 6, 15, 10, 30, tzinfo=ZoneInfo("America/Denver"))],
[datetime(2023, 6, 15, 10, 30, tzinfo=ZoneInfo("Asia/Tokyo"))],
]
client.insert("events", data, column_names=["event_time"])
results = client.query(
"SELECT event_time FROM events ORDER BY event_time",
query_tz="UTC",
tz_mode="aware",
)
assert [row[0].hour for row in results.result_rows] == [1, 10, 16]Objets datetime sans fuseau horaire
Le paramètre global naive_datetime_insert contrôle l'insertion d'objets Python natifs contenant des valeurs datetime sans fuseau horaire. Il s'applique également aux chaînes ISO sans fuseau horaire acceptées par les colonnes DateTime64.
"local"est la valeur par défaut dans la version 1.x. Python interprète la valeur dans le fuseau horaire du processus lors de l'appel à.timestamp(). Cela préserve le comportement existant."server"interprète la valeur comme une heure locale dans le fuseau horaire déclaré par la colonneDateTimeouDateTime64. Si la colonne n'a pas de fuseau horaire, le fuseau horaire du serveur communiqué lors de la connexion du client est utilisé.
Définissez l'option avant une insertion. Elle est lue lors de la sérialisation de chaque colonne d'insertion native contenant des objets Python datetime ou des chaînes ISO DateTime64 ; la modification s'applique donc aux clients existants et aux contextes d'insertion réutilisables.
from datetime import datetime
from clickhouse_connect import common
common.set_setting("naive_datetime_insert", "server")
naive_time = datetime(2023, 6, 15, 10, 30)
client.insert("events", [[naive_time]], column_names=["event_time"])Avec "server", ClickHouse Connect associe le tzinfo cible avant de convertir la valeur en époque Unix. Pour les fuseaux horaires IANA, il suit les règles de la bibliothèque standard pour les transitions vers ou depuis l’heure d’été. En cas de chevauchement à l’automne, la valeur fold du datetime est utilisée. Par défaut, fold=0 sélectionne le décalage avant la transition, tandis que fold=1 sélectionne celui après la transition. En cas de lacune au printemps, la même sélection de décalage est utilisée, sans rejet ni normalisation.
Les heures locales inexistantes lors d’une lacune printanière peuvent ne pas effectuer d’aller-retour via un paramètre de requête en mode wall, car l’analyse de texte de ClickHouse peut sélectionner un décalage différent. Utilisez un datetime avec fuseau horaire ou une heure locale valide lorsque l’instant est important.
L’option s’applique uniquement aux insertions natives d’objets Python datetime et aux chaînes ISO sans fuseau horaire acceptées par DateTime64. Les colonnes NumPy et Pandas de type datetime64 sans fuseau horaire conservent leur conversion existante des heures locales UTC.
Pour représenter un instant précis indépendamment de l’un ou l’autre mode, associez le fuseau horaire souhaité ou fournissez explicitement un entier époque Unix.
from datetime import datetime, timezone
utc_time = datetime(2023, 6, 15, 10, 30, tzinfo=timezone.utc)
client.insert("events", [[utc_time]], column_names=["event_time"])
naive_time = datetime(2023, 6, 15, 10, 30)
epoch_timestamp = int(naive_time.replace(tzinfo=timezone.utc).timestamp())
client.insert("events", [[epoch_timestamp]], column_names=["event_time"])Les paramètres de requête datetime sans fuseau horaire utilisent le paramètre distinct naive_datetime_binding. Son mode par défaut, "wall", envoie les champs tels quels, sans conversion vers le fuseau horaire local de l'hôte. Consultez la section Argument Parameters.
Colonnes DateTime avec métadonnées de fuseau horaire
Les colonnes ClickHouse peuvent déclarer des métadonnées de fuseau horaire, par exemple DateTime('America/Denver') ou DateTime64(3, 'Asia/Tokyo'). Ces métadonnées déterminent la manière dont les valeurs sont affichées lors de l’exécution d’une requête.
Lors de l’insertion d’une valeur avec fuseau horaire, ClickHouse Connect préserve l’instant représenté. Pour une valeur sans fuseau horaire, le paramètre naive_datetime_insert détermine si le fuseau horaire du processus ou celui de la colonne est utilisé. Lors d’une requête, le résultat utilise le fuseau horaire de la colonne, sauf si une substitution par colonne est fournie via l’argument column_tzs. L’argument query_tz ne remplace pas le fuseau horaire déclaré pour une colonne.
from datetime import datetime
from zoneinfo import ZoneInfo
client.command(
"CREATE TABLE events_with_timezone "
"(event_time DateTime('America/Los_Angeles')) "
"ENGINE Memory"
)
data = datetime(2023, 6, 15, 10, 30, tzinfo=ZoneInfo("America/New_York"))
client.insert("events_with_timezone", [[data]], column_names=["event_time"])
result = client.query("SELECT event_time FROM events_with_timezone")
returned = result.first_row[0]
assert returned.hour == 7
assert returned.tzinfo == ZoneInfo("America/Los_Angeles")Insertions de fichiers
clickhouse_connect.driver.tools.insert_file transmet en flux un fichier local vers une table existante et confie l’analyse à ClickHouse.
| Paramètre | Type | Par défaut | Description |
|---|---|---|---|
client |
Client |
Obligatoire | Client synchrone utilisé pour l’insertion. |
table |
str | Obligatoire | Table cible simple ou qualifiée par la base de données. |
file_path |
str | Obligatoire | Chemin local vers le fichier d’entrée. |
fmt |
str | "CSV" ou "CSVWithNames" |
Format d’entrée. Par défaut, "CSV" lorsque column_names est fourni, et "CSVWithNames" sinon. |
column_names |
Sequence[str] | None |
Colonnes représentées par le fichier. Non requis pour les formats qui incluent les noms. |
database |
str | None |
Base de données cible lorsque la table n’est pas qualifiée. |
settings |
dict | None |
Voir l’argument Settings. |
compression |
str | None |
Compression existante du fichier, telle que "zstd", "lz4" ou "gzip". gzip est inféré à partir des noms de fichier .gz et .gzip. |
Les paramètres du format d’entrée, tels que input_format_allow_errors_ratio et input_format_allow_errors_num, peuvent être transmis via settings.
import clickhouse_connect
from clickhouse_connect.driver.tools import insert_file
client = clickhouse_connect.get_client()
insert_file(
client,
"example_table",
"my_data.csv",
settings={
"input_format_allow_errors_ratio": 0.2,
"input_format_allow_errors_num": 5,
},
)Pour un AsyncClient, utilisez await avec insert_file_async en passant les mêmes arguments :
from clickhouse_connect.driver.tools import insert_file_async
await insert_file_async(async_client, "example_table", "my_data.csv")L'utilitaire asynchrone lit le fichier dans un thread worker avant d'attendre raw_insert, de sorte que le contenu du fichier reste en mémoire.