Learn Labs
Ingestion & Other Engines

Insert & Export Formats

Native, JSONEachRow, CSV, Parquet

Most databases speak one wire format and one bulk-load format. Every query in and out of ClickHouse — INSERT, SELECT, even the results printed to your terminal — passes through a pluggable format, and ClickHouse supports dozens of them. Picking the right one for the right job is a real performance decision, not just a convenience.

The formats you actually reach for

  • JSONEachRow — one JSON object per line, no surrounding array or commas. The default choice for application-side inserts, because almost every language can produce it without a ClickHouse-specific library.
  • CSV / TSV — plain delimited text. Universally producible, but the slowest to parse and the worst at preserving types (everything round-trips through text).
  • Native — ClickHouse's own binary, columnar wire format. This is what clickhouse-client and server-to-server replication use by default, and it's the fastest option because data is already shaped the way the engine stores it — no row-to-column transposition needed on the way in.
  • RowBinary — binary, but row-oriented: values in declaration order, no field names, no delimiters. Faster than CSV to parse, more compact, but you must get column order exactly right since there's nothing self-describing about it.
  • Parquet / Arrow — columnar formats from the wider data-lake ecosystem. ClickHouse reads and writes both natively, which makes it easy to sit next to Spark, Pandas, or a data lake on S3 without a separate conversion step.

Using a format

Every format is invoked the same way: a FORMAT clause on the query.

-- clickhouse-client
INSERT INTO events FORMAT JSONEachRow
{"user_id": 102, "event": "click", "ts": "2026-08-05 10:01:00"}
{"user_id": 204, "event": "view",  "ts": "2026-08-05 10:02:00"}

SELECT * FROM events LIMIT 5 FORMAT Pretty;

The same thing over the HTTP interface (port 8123, covered in Docker & Setup) is just a POST body:

curl 'http://localhost:8123/?query=INSERT+INTO+events+FORMAT+JSONEachRow' \
--data-binary '
  {"user_id": 102, "event": "click", "ts": "2026-08-05 10:01:00"}
  {"user_id": 204, "event": "view",  "ts": "2026-08-05 10:02:00"}
'

Reading files directly, without an INSERT

Table functions like file(), s3(), and url() let a format double as a way to query external data as if it were a table, no loading step required:

SELECT count()
FROM s3('https://bucket.s3.amazonaws.com/events/*.parquet', 'Parquet');

INSERT INTO events
SELECT * FROM file('/data/backfill.csv', 'CSV');

These table functions are the query-time counterpart to the integration engines (S3, MySQL, PostgreSQL, URL, File) — a table function reads once for a single query; an engine defines a permanent table you can query repeatedly.

On this page