Your first table and query
You need a running ClickHouse server and an open client session. See Install ClickHouse if you do not have one yet.
Group your tables in a database of their own:
CREATE DATABASE IF NOT EXISTS helloworldDefine columns with explicit types, and pick a table engine:
CREATE TABLE helloworld.my_first_table
(
user_id UInt32,
message String,
timestamp DateTime,
metric Float32
)
ENGINE = MergeTree()
PRIMARY KEY (user_id, timestamp)user_id is a 32-bit unsigned integer. String replaces types like VARCHAR, BLOB and CLOB from other database systems and takes no length. metric is a 32-bit float.
The table engine determines how and where data is stored, which queries are supported, and whether data is replicated. For a table on a single-node server, MergeTree is the choice.
Insert several rows in one statement. Batching matters — see inserting data for why:
INSERT INTO helloworld.my_first_table (user_id, message, timestamp, metric) VALUES
(101, 'Hello, ClickHouse!', now(), -1.0 ),
(102, 'Insert a lot of rows per batch', yesterday(), 1.41421 ),
(102, 'Sort your data based on your commonly-used queries', today(), 2.718 ),
(101, 'Granules are the smallest chunks of data read', now() + 5, 3.14159 )SELECT *
FROM helloworld.my_first_table
ORDER BY timestamp┌─user_id─┬─message────────────────────────────────────────────┬───────────timestamp─┬──metric─┐
│ 102 │ Insert a lot of rows per batch │ 2022-03-21 00:00:00 │ 1.41421 │
│ 102 │ Sort your data based on your commonly-used queries │ 2022-03-22 00:00:00 │ 2.718 │
│ 101 │ Hello, ClickHouse! │ 2022-03-22 14:04:09 │ -1 │
│ 101 │ Granules are the smallest chunks of data read │ 2022-03-22 14:04:14 │ 3.14159 │
└─────────┴────────────────────────────────────────────────────┴─────────────────────┴─────────┘
4 rows in set. Elapsed: 0.008 sec.Change the output format#
Add a FORMAT clause to get the same result in a different shape. ClickHouse supports over 70 input and output formats:
SELECT *
FROM helloworld.my_first_table
ORDER BY timestamp
FORMAT TabSeparatedThe same formats work over the HTTP interface, which is how most applications talk to ClickHouse.
What the primary key just did#
The PRIMARY KEY (user_id, timestamp) clause did not create a uniqueness constraint. Primary keys in ClickHouse are not unique for each row — the table above holds two rows with user_id = 101.
Instead, the primary key determines how rows are sorted when written to disk. Every 8,192 rows — the index granularity — ClickHouse writes one entry into the primary key index file. That sparse index is what lets a query skip most of the table without reading it.
If you omit PRIMARY KEY, the ORDER BY tuple becomes the primary key. If you specify both, the primary key must be a prefix of the sort order.
Choosing those columns well is the single highest-impact decision in a ClickHouse schema. On a 60-million-row table, the right key turns a 0.055-second scan into a 0.013-second one. See choosing a primary key.
Next steps#
- Choose a primary key — pick the columns that make your queries fast
- The sparse primary index — how granule skipping works
- Insert data efficiently — batch sizes and async inserts