Inserção de dados com ClickHouse Connect: uso avançado
InsertContexts
O ClickHouse Connect executa inserções no formato Native, pelos métodos insert e insert_df, em um InsertContext. Os métodos insert_arrow, insert_df_arrow e raw_insert enviam seus payloads diretamente e não usam um InsertContext. O InsertContext inclui todos os valores enviados como argumentos para o método insert do cliente. Além disso, quando um InsertContext é criado pela primeira vez, o ClickHouse Connect recupera os tipos de dados das colunas de inserção necessários para inserções eficientes no formato Native. Ao reutilizar o InsertContext em várias inserções, essa "pré-consulta" é evitada, e as inserções são executadas com mais rapidez e eficiência.
Um InsertContext pode ser obtido usando o método create_insert_context do cliente. O método recebe os mesmos argumentos que a função insert, exceto o próprio context. Observe que, para reutilização, apenas a propriedade data dos InsertContexts deve ser modificada. Isso está de acordo com seu propósito de fornecer um objeto reutilizável para inserções repetidas de novos dados na mesma tabela.
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 incluem estado mutável que é atualizado durante o processo de insert, portanto não são thread-safe.
Formatos de escrita
Os formatos de escrita são implementados para um número limitado de tipos. Na maioria dos casos, o ClickHouse Connect determina automaticamente o formato de escrita correto para uma coluna com base no primeiro valor de dados não nulo. Por exemplo, quando o primeiro valor de uma coluna DateTime é um inteiro, o cliente o trata como um segundo desde a epoch.
Normalmente, não é necessário substituir um formato de escrita, mas os métodos em clickhouse_connect.datatypes.format podem definir um globalmente. Wrappers de contêiner, como Array, Nullable e LowCardinality, preservam o comportamento de formatação do tipo do elemento.
Opções de formato de escrita
| ClickHouse Type | Tipo nativo do Python | Formatos de escrita | Comentários |
|---|---|---|---|
| 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 | Uma coluna deve conter texto ou bytes de forma consistente. | |
| FixedString | bytes | string | Valores String são preenchidos com bytes zero. Bytes vazios são gravados como bytes todos zero. |
| Enum[8,16] | str or int | Insira labels como strings ou seus valores inteiros subjacentes. | |
| Date | datetime.date | int | Valores inteiros são interpretados como dias desde 1970-01-01. |
| Date32 | datetime.date | int | Valores inteiros são interpretados como deslocamentos de dias com sinal. |
| DateTime | datetime.datetime | int | Valores inteiros são interpretados como segundos desde a epoch. |
| DateTime64 | datetime.datetime | int | Valores inteiros são interpretados como ticks na precisão da coluna. |
| Time | datetime.timedelta | int, string, time | Valores inteiros são interpretados como segundos. |
| Time64 | datetime.timedelta | int, string, time | Valores inteiros são interpretados como ticks na precisão da coluna. |
| IPv4 | ipaddress.IPv4Address |
string | Strings formatadas corretamente podem ser inseridas como endereços IPv4 |
| IPv6 | ipaddress.IPv6Address |
string | Strings formatadas corretamente podem ser inseridas como endereços IPv6 |
| Tuple | dict or tuple | ||
| Map | dict | ||
| Nested | Sequence[dict] | ||
| UUID | uuid.UUID | string | Strings formatadas corretamente podem ser inseridas como UUIDs do ClickHouse |
| JSON | dict | string | Há suporte a dicionários e strings de objetos JSON. O tipo legado Object('json') não é suportado. |
| Variant | object | Os valores usam a serialização nativa do tipo membro. Use clickhouse_connect.datatypes.dynamic.typed_variant quando os tipos Python forem ambíguos. |
|
| Dynamic | object | No momento, os valores são inseridos por meio de sua representação String. | |
| QBit | Sequence[float] | O NumPy é usado automaticamente para uma transposição de bits mais rápida quando instalado. |
Métodos de inserção especializados
O ClickHouse Connect fornece métodos de inserção especializados para formatos de dados comuns:
insert_df– Insere um DataFrame do Pandas como dados Native orientados a colunas. Também oferece suporte a nomes/tipos de coluna explícitos ou a umInsertContextreutilizável.insert_arrow– Insere uma tabela PyArrow usando o formato de entrada Arrow do ClickHouse.insert_df_arrow– Insere um DataFrame do Pandas com Arrow como backend ou um DataFrame do Polars. Todas as colunas do Pandas devem usar backendsdtypebaseados em Arrow.
Todos os três métodos aceitam database, settings e transport_settings de HTTP por solicitação.
Inserção de DataFrame do 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)Inserção de tabela 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)Inserção de DataFrame com Arrow como backend (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)Criar uma tabela a partir de um esquema do PyArrow
create_table_from_arrow_schema gera uma instrução CREATE TABLE a partir de campos escalares comuns do Arrow. O mapeamento abrange inteiros com e sem sinal, valores de ponto flutuante, booleanos, strings, datas e timestamps. Ele cria intencionalmente colunas do ClickHouse que não permitem NULL e gera TypeError para tipos do Arrow sem suporte, portanto revise o DDL gerado antes de executá-lo.
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)Fusos horários
Ao inserir objetos datetime do Python em colunas DateTime ou DateTime64, o ClickHouse Connect os converte em valores de epoch.
Objetos datetime com fuso horário
Objetos com fuso horário preservam o instante representado. O fuso horário de origem não precisa coincidir com o fuso horário definido na coluna do 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]Objetos datetime sem fuso horário
A configuração global naive_datetime_insert controla inserções nativas de objetos Python com valores datetime sem fuso horário. Ela também se aplica a strings ISO sem fuso horário aceitas por colunas DateTime64.
"local"é o padrão na versão 1.x. O Python interpreta o valor no fuso horário do processo quando.timestamp()é chamado. Isso preserva o comportamento atual."server"interpreta o valor como hora do relógio no fuso horário declarado pela colunaDateTimeouDateTime64. Se a coluna não tiver fuso horário, usa o fuso horário do servidor informado quando o cliente se conectou.
Defina a opção antes de uma inserção. Ela é lida quando cada coluna de inserção nativa que contém objetos datetime do Python ou strings ISO DateTime64 é serializada; portanto, a alteração se aplica a clientes existentes e contextos de inserção reutilizáveis.
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"])Com "server", o ClickHouse Connect associa o tzinfo de destino antes de converter o valor em epoch. Para fusos horários IANA, segue as regras da biblioteca padrão para transições de horário de verão. Uma sobreposição no outono usa o valor fold do datetime. Por padrão, fold=0 seleciona o deslocamento anterior à transição, enquanto fold=1 seleciona o deslocamento posterior. Uma lacuna na primavera usa a mesma seleção de deslocamento e não é rejeitada nem normalizada.
Horários de relógio inexistentes na lacuna da primavera podem não ser preservados em uma conversão de ida e volta por meio de um parâmetro de consulta no modo de relógio, pois a análise de texto do ClickHouse pode selecionar um deslocamento diferente. Use um datetime com fuso horário ou um horário de relógio válido quando o instante for importante.
A opção se aplica apenas a inserções nativas de objetos Python datetime e strings ISO sem fuso horário aceitas por DateTime64. Colunas NumPy e Pandas com dtype datetime64 sem fuso horário mantêm a conversão existente de horário de relógio em UTC.
Para representar um instante específico independentemente de qualquer modo, associe o fuso horário desejado ou forneça explicitamente um inteiro epoch.
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"])Parâmetros de consulta datetime sem fuso horário usam a configuração separada naive_datetime_binding. O modo padrão "wall" envia os campos de data e hora sem conversão para o horário local do host. Consulte a seção argumento Parameters.
Colunas DateTime com metadados de fuso horário
As colunas do ClickHouse podem declarar metadados de fuso horário, por exemplo DateTime('America/Denver') ou DateTime64(3, 'Asia/Tokyo'). Esses metadados controlam como os valores são apresentados quando são consultados.
Ao inserir um valor com fuso horário, o ClickHouse Connect preserva o instante representado. Para um valor sem fuso horário, a configuração naive_datetime_insert controla se é usado o fuso horário do processo ou o da coluna. Ao consultar, o resultado usa o fuso horário da coluna, a menos que seja fornecido um override por coluna com o argumento column_tzs. O argumento query_tz não substitui o fuso horário declarado de uma coluna.
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")Inserções de arquivo
clickhouse_connect.driver.tools.insert_file transmite um arquivo local para uma tabela existente em fluxo e delega o parsing ao ClickHouse.
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
client |
Client |
Obrigatório | Client síncrono usado para a inserção. |
table |
str | Obrigatório | Tabela de destino simples ou qualificada com o database. |
file_path |
str | Obrigatório | Caminho local para o arquivo de entrada. |
fmt |
str | "CSV" ou "CSVWithNames" |
Formato de entrada. O padrão é "CSV" quando column_names é fornecido e "CSVWithNames" caso contrário. |
column_names |
Sequence[str] | None |
Colunas representadas pelo arquivo. Não é necessário para formatos que incluem nomes. |
database |
str | None |
Database de destino quando a tabela não é qualificada. |
settings |
dict | None |
Consulte Argumento settings. |
compression |
str | None |
Compressão existente do arquivo, como "zstd", "lz4" ou "gzip". gzip é inferido a partir de nomes de arquivo .gz e .gzip. |
Configurações de formato de entrada, como input_format_allow_errors_ratio e input_format_allow_errors_num, podem ser passadas por meio de 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,
},
)Para um AsyncClient, use await com insert_file_async e os mesmos argumentos:
from clickhouse_connect.driver.tools import insert_file_async
await insert_file_async(async_client, "example_table", "my_data.csv")O helper assíncrono lê o arquivo em uma thread de trabalho antes de aguardar o raw_insert, portanto o conteúdo do arquivo permanece na memória.