KDB knowledge

Quick start

Get kdb, make a database, create a table, add rows, query them and open the web console.

KDB is one program: kdb (kdb.exe on Windows), with three folders of plain files next to it: web/ (the web console), help/ (these pages) and skill/ (the Claude skill). It keeps each database in a folder. You talk to it with SQL (the language of databases). This page takes about 10 minutes.

KDB is a general-purpose database: market and time-series data, sensor readings, logs and metrics, accounting, users and logins, app backends. A small example: a table of daily stock prices, three rows, and a query.

Example: Your first table, rows and query

CREATE TABLE ochl_day (
  time TIMESTAMP, symbol SYMBOL,
  open DOUBLE, high DOUBLE, low DOUBLE, close DOUBLE, volume DOUBLE
) TIMESERIES(time) PARTITION BY YEAR DEDUP KEY (symbol, time);

INSERT INTO ochl_day VALUES
  ('2026-09-09', 'CITY', 512, 512.4, 504, 504.6, 182390),
  ('2026-09-10', 'CITY', 504.6, 507, 500.7, 504.9, 183040),
  ('2026-09-10', 'COAST', 380.5, 382, 375.2, 376.1, 186200);

SELECT time, symbol, close FROM ochl_day WHERE symbol = 'CITY';

Result:

CREATE TABLE ochl_day (

table 'ochl_day' created

INSERT INTO ochl_day VALUES ('2026-09-09', 'CITY', 512, 512.4, 504, 504.6, 182390), ('2026-09-10', '

3 row(s) inserted

SELECT time, symbol, close FROM ochl_day WHERE symbol = 'CITY'

time (TIMESTAMP)symbol (SYMBOL)close (DOUBLE)
2026-09-09T00:00:00.000ZCITY504.6
2026-09-10T00:00:00.000ZCITY504.9

(2 rows)

Get kdb

There are three ways. Pick one.

WayYou runThen
An install packageunpack kdb-<version>-windows-x64.zip (or the Linux .tar.gz) and run install.ps1 (or install.sh)kdb is on your PATH, with its web, help and skill folders
Dockerdocker run with the image kdb (below)the server runs on port 9900, the PostgreSQL port on 8832
Build from the sourcecmake (below)the program is in build\bin\kdb.exe

Install packages explains the packages and the Docker image in full.

Docker

Syntax:

docker run -d --name kdb -p 9900:9900 -p 8832:8832 -e KDB_TOKEN=<secret> -v <data>:/data kdb

The image holds the program and its three folders. Make it once from the source folder with docker build -t kdb -f docker/Dockerfile .. The data stays in the volume, so a new container keeps it.

docker run -d --name kdb --restart unless-stopped --stop-timeout 60 \
  -p 9900:9900 -p 8832:8832 -e KDB_TOKEN=my-secret -v kdb-data:/data kdb
curl http://127.0.0.1:9900/health

Then open http://127.0.0.1:9900 and go on at Open the web console: the console and the HTTP API do everything the kdb sql steps below do.

Build from the source

Syntax:

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build --target kdb

Build the program from the KDB source folder. You need CMake, Ninja and a C++17 compiler (on Windows: MSYS2 UCRT64). KDB needs no other libraries.

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build --target kdb
$env:Path = "$PWD\build\bin;" + $env:Path

Check that it works:

Command line: Show the version

kdb version

Output:

kdb 0.2.0-dev (crc32c hardware: yes)
tls: OpenSSL 3.6.3 9 Jun 2026

Stored-format features this build reads (51):
  views                0.2.0-dev (2026-09-10)   CREATE VIEW: the "views" file
  partition_format_2   0.2.0-dev (2026-09-11)   table partition files in format 2 (one raw block per column)
  block_stats          0.2.0-dev (2026-09-11)   column stats section in partition files (count, min, max, sum per column)
  runs                 0.2.0-dev (2026-09-11)   log record 'C': rows of a bulk upsert kept as columns
  auto_meta            0.2.0-dev (2026-09-11)   AUTO id counters in the manifest (auto.<table id>)
  json                 0.2.0-dev (2026-09-11)   JSON columns (type code 12)
  bytes                0.2.0-dev (2026-09-11)   BYTES columns (type code 13)
  check                0.2.0-dev (2026-09-11)   CHECK constraints (catalog format 2, part 'C')
  indexes              0.2.0-dev (2026-09-11)   CREATE INDEX, UNIQUE and FULLTEXT indexes (catalog part 'I')
  routines             0.2.0-dev (2026-09-11)   functions, procedures and triggers: the "routines" file
  users                0.2.0-dev (2026-09-11)   server users and roles (_system.users)
  audit                0.2.0-dev (2026-09-12)   SET AUDIT ON: catalog part 'A', log record 'P', the _audit_log table
  table_grants         0.2.0-dev (2026-09-12)   GRANT ... ON TABLE (_system.users.table_grants)
  totp                 0.2.0-dev (2026-09-12)   two-factor logins (_system.users.totp)
  trash                0.2.0-dev (2026-09-12)   dropped tables kept as _dropped_<ms>_<name> for UNDROP TABLE
  database_settings    0.2.0-dev (2026-09-12)   ALTER DATABASE ... SET READ ONLY (_system.database_settings)
  dialects             0.2.0-dev (2026-09-12)   ALTER DATABASE ... SET dialect (_system.database_dialects)
  pg_catalog_parts     0.2.0-dev (2026-09-12)   PostgreSQL column types, multi-column keys, foreign keys, expression and partial indexes (catalog parts 'T', 'K', 'F', 'X')
  sequences            0.2.0-dev (2026-09-12)   CREATE SEQUENCE: the "sequences" file and its log records
  batched_log_sync     0.2.0-dev (2026-09-11)   log segments in the batched format (header flag 1, 16-byte commit records): group commit and --sync-commits batch
  dedup_change         0.2.0-dev (2026-09-12)   ALTER TABLE ... DEDUP ENABLE / DISABLE: the DEDUP KEY of a table that already holds rows
  plpgsql_triggers     0.2.0-dev (2026-09-12)   PostgreSQL plpgsql trigger functions and FOR EACH ROW triggers in the "routines" file
  cached_views         0.2.0-dev (2026-09-13)   CREATE CACHED VIEW: which views are cached, and REFRESH ON COMMIT, in the "views" file
  deferred_triggers    0.2.0-dev (2026-09-13)   CREATE TRIGGER ... DEFERRED: job records in the log, and the "trigger_queue" and "q/<job>" files of queued jobs
  materialized_views   0.2.0-dev (2026-09-13)   CREATE MATERIALIZED VIEW: catalog part 'M', REFRESH MATERIALIZED VIEW log records, mv_dirty.<id> in the manifest
  table_ttl            0.2.0-dev (2026-09-13)   TTL n DAYS on a table: catalog part 'L'
  read_only_procedures 0.2.0-dev (2026-09-24)   CREATE PROCEDURE ... READ ONLY in the "routines" file
  dedup_insert         0.2.0-dev (2026-09-24)   WITH (dedup_insert = 'upsert') on a table: catalog part 'U'
  decimal128           0.2.0-dev (2026-09-25)   NUMERIC(p, s) columns with more than 18 digits: 128-bit DECIMAL (type code 14) in the catalog, partition files and log
  dedup_generated_key  0.2.0-dev (2026-09-25)   a stored generated column in a DEDUP KEY, like the minute of the time (an older build refuses such a catalog)
  symbol_partitions    0.2.0-dev (2026-09-25)   PARTITION BY <time>, SYMBOL(col): catalog part 'P' (one partition file per time bucket and symbol, t/<id>/<time>/s-...)
  round_time_zone      0.2.0-dev (2026-09-25)   TIMESTAMP ROUND ... TIME ZONE 'zone' columns: catalog part 'R'
  enum_types           0.2.0-dev (2026-09-25)   CREATE TYPE ... AS ENUM: the ENUM types after the tables of the catalog (catalog format 3)
  row_ttl              0.2.0-dev (2026-09-25)   CREATE TABLE ... WITH (ttl_column = c): rows expire one by one, catalog part 'E'
  column_compression   0.2.0-dev (2026-09-25)   ALTER TABLE ... SET (compression = on): catalog part 'Z' and packed partition files (header flag 2)
  calendars            0.2.0-dev (2026-09-25)   CREATE CALENDAR: trading calendars (eras, holidays, extra trading days) after the ENUM types of the catalog (catalog format 4)
  materialized_view_sample_by 0.2.0-dev (2026-09-26)   CREATE MATERIALIZED VIEW ... AS (SELECT ... SAMPLE BY ...): a SAMPLE BY query in catalog part 'M', refreshed bucket by bucket
  comments             0.2.0-dev (2026-09-26)   COMMENT ON TABLE / COLUMN: the comments of a table and its columns, catalog part 'N'
  cache_tables         0.2.0-dev (2026-10-02)   WITH (max_bytes, evict, durability): cache tables with a memory cap and eviction, catalog part 'Y'
  resident_tables      0.2.0-dev (2026-10-02)   WITH / SET (resident = on): a table never unloaded to make room, catalog part 'H'
  partition_tails      0.2.0-dev (2026-10-02)   tail files: rows appended to a partition saved in extra files t/<id>/<partition>.tail<k>.b<version> (tail_files)
  uuid                 0.2.0-dev (2026-10-02)   UUID columns: 16 bytes per value (type code 15) in the catalog, partition files and log
  search_analyzer      0.2.0-dev (2026-10-02)   CREATE FULLTEXT INDEX ... WITH (language = 'english'): the word analyzer of a full-text index, catalog part 'S'
  column_grants        0.2.0-dev (2026-10-02)   GRANT / REVOKE on single columns (_system.users.column_grants)
  row_policies         0.2.0-dev (2026-10-02)   row-level security: CREATE POLICY and ALTER TABLE ... ENABLE ROW LEVEL SECURITY (_system.row_policies)
  partition_deltas     0.2.0-dev (2026-10-02)   delta files: a few rows of a partition replaced or deleted, saved in t/<id>/<partition>.delta.b<version>
  fulltext_segments    0.2.0-dev (2026-10-02)   full-text and JSON index segments in fts/*.kfs (the words of each partition's rows; never recorded: an older build ignores them)
  compressed_log       0.2.0-dev (2026-10-02)   packed log records (type 3): the data records of a big commit written compressed (KDB_WAL_COMPRESS)
  endpoints            0.2.0-dev (2026-10-03)   CREATE ENDPOINT: saved queries served at GET /v/<name>, kind 'E' in the routines file
  rules                0.2.0-dev (2026-10-03)   CREATE RULE: named checks, session hours and settings of a table (catalog part 'Q')
  sync_commit_tables   0.2.0-dev (2026-10-03)   WITH (synchronous_commit = on | off) of a table: catalog part 'G'

A database that uses a feature not in this list needs a newer KDB (kdb check exits with 3).

Your first database

Syntax:

kdb sql database "SQL"

kdb sql opens a database, runs the SQL, prints the result and closes the database. When the database does not exist yet, KDB makes a new empty one.

ArgumentTypeMeaning
databasename or foldera database name like shop, or a folder path like D:\kdb\shop
"SQL"textone or more statements, separated by ;

Command line: Make a table in a new database, add a row, read it

kdb sql shop "CREATE TABLE brokers (id INT PRIMARY KEY, name STRING NOT NULL)"
kdb sql shop "INSERT INTO brokers VALUES (58, 'Northgate Securities'), (45, 'Imperial Securities')"
kdb sql shop "SELECT id, name FROM brokers ORDER BY id"

Output:

> kdb sql shop "CREATE TABLE brokers (id INT PRIMARY KEY, name STRING NOT NULL)"

table 'brokers' created

> kdb sql shop "INSERT INTO brokers VALUES (58, 'Northgate Securities'), (45, 'Imperial Securities')"

2 row(s) inserted

> kdb sql shop "SELECT id, name FROM brokers ORDER BY id"

id | name                
---+---------------------
45 | Imperial Securities 
58 | Northgate Securities
(2 rows, 0.3 ms)

A mistake gives a clear message and exit code 1:

Command line: A typing mistake in a table name (shows an error)

kdb sql shop "CREATE TABLE brokers (id INT PRIMARY KEY, name STRING NOT NULL)"
kdb sql shop "SELECT * FROM brokerz"

Output:

> kdb sql shop "CREATE TABLE brokers (id INT PRIMARY KEY, name STRING NOT NULL)"

table 'brokers' created

> kdb sql shop "SELECT * FROM brokerz"

error: line 1, column 15: table or view 'brokerz' does not exist

exit code 1

CREATE TABLE, INSERT and SELECT

Syntax:

CREATE TABLE name (column TYPE [rules], ...) [TIMESERIES(time_column) ...]
INSERT INTO name VALUES (value, ...), (value, ...)
SELECT columns FROM name [WHERE condition]

The three statements you use most. CREATE TABLE makes a table. INSERT adds rows. SELECT reads rows.

Example: App users: a relational table

CREATE TABLE users (
  id    LONG PRIMARY KEY AUTO,
  email STRING NOT NULL UNIQUE,
  name  STRING
);
INSERT INTO users (email, name) VALUES ('ram@example.com', 'Ram'), ('sita@example.com', 'Sita');
SELECT id, email, name FROM users;

Result:

CREATE TABLE users (

table 'users' created

INSERT INTO users (email, name) VALUES ('ram@example.com', 'Ram'), ('sita@example.com', 'Sita')

2 row(s) inserted

SELECT id, email, name FROM users

id (LONG)email (STRING)name (STRING)
1ram@example.comRam
2sita@example.comSita

(2 rows)

Example: A local time goes in with its offset (+05:45) and comes out in UTC

CREATE TABLE live_prices (time TIMESTAMP, symbol SYMBOL, ltp DOUBLE) TIMESERIES(time);
INSERT INTO live_prices VALUES ('2026-09-10T11:00:00+05:45', 'CITY', 513.0);
SELECT time, symbol, ltp FROM live_prices;

Result:

CREATE TABLE live_prices (

table 'live_prices' created

INSERT INTO live_prices VALUES ('2026-09-10T11:00:00+05:45', 'CITY', 513.0)

1 row(s) inserted

SELECT time, symbol, ltp FROM live_prices

time (TIMESTAMP)symbol (SYMBOL)ltp (DOUBLE)
2026-09-10T05:15:00.000ZCITY513

(1 row)

kdb shell

Syntax:

kdb shell [database]

A prompt where you type SQL. End each statement with ;. A statement can use many lines. Commands that start with a dot (.tables, .help, .exit) need no ;.

Command line: Type SQL in the shell

kdb shell shop
> CREATE TABLE posts (id LONG PRIMARY KEY AUTO, author STRING, body STRING);
> INSERT INTO posts (author, body) VALUES ('ram', 'CITY dividend looks good.');
> .tables
> SELECT id, author, body FROM posts;
> .exit

Output:

KDB 0.2.0-dev shell on shop
End statements with ';'. Type .help for commands, .exit to quit.
kdb> table 'posts' created
kdb> 1 row(s) inserted
kdb> name  | kind       | partition | columns | rows
------+------------+-----------+---------+-----
posts | relational | NONE      |       3 |    1
(1 row, 0.0 ms)
kdb> id | author | body                     
---+--------+--------------------------
 1 | ram    | CITY dividend looks good.
(1 row, 0.2 ms)
kdb>

Load the sample data

Syntax:

Get-Content file.sql -Raw | kdb sql database -

The tutorial has a small made-up stock market dataset: securities, brokers, daily and minute candles, a trade log and live prices. It is just one example of what KDB stores. The - means "read the SQL from the pipe". Run the two files once, in this order.

# from the KDB source folder
$examples = "docs\tutorial\examples"
Get-Content "$examples\setup\schema.sql" -Raw | kdb sql market -
Get-Content "$examples\setup\sample-data.sql" -Raw | kdb sql market -

Command line: The tables of the sample data (sample data: sample)

kdb sql market "SHOW TABLES"

Output:

name         | kind       | partition | columns | rows
-------------+------------+-----------+---------+-----
brokers      | relational | NONE      |       2 |    5
live_data    | timeseries | DAY       |      12 |   12
ochl_day     | timeseries | YEAR      |       7 |   57
ochl_minutes | timeseries | MONTH     |       7 |  956
securities   | relational | NONE      |       3 |    5
trade_log    | timeseries | DAY       |       8 |   32
(6 rows, 0.0 ms)

Example: The last 3 daily closes of CITY (sample data: sample)

SELECT time, symbol, close FROM ochl_day WHERE symbol = 'CITY' ORDER BY time DESC LIMIT 3;

Result:

time (TIMESTAMP)symbol (SYMBOL)close (DOUBLE)
2026-09-10T00:00:00.000ZCITY504.9
2026-09-09T00:00:00.000ZCITY504.6
2026-09-08T00:00:00.000ZCITY497.5

(3 rows)

Open the web console (kdb serve)

Syntax:

kdb serve [database] [--port 9900]

Starts the server. Open http://127.0.0.1:9900 in a browser. Type SQL, press Ctrl+Enter, and see a grid or a chart. Press Ctrl+C to stop the server.

Command line: Start the server on the sample data (sample data: sample)

kdb serve market

Output:

KDB 0.2.0-dev
  console   http://127.0.0.1:9900
  data      market
  backups   ./_backups/market (from default)
  access    no token
  capacity  32 HTTP threads (default), 500 PostgreSQL connections (default), lock wait 5 s (default)
  lanes     reads using 100 ms of CPU (three times in a row) wait for one of 16 heavy slots, at most 16 HTTP in line (--lanes on)
  reads     32 reads run at once (the CPUs, at least 2; follows the CPU limit), the rest wait in line (--read-slots)
  commits   synced to disk before they are confirmed (--sync-commits always)
  time zone Asia/Katmandu (the computer's); also timers and backup schedules
  readers   read while a long trigger or CALL writes (--reader-yield on); idle open transaction rolled back after 30 s (default)
  memory    26.95 GB for the whole process (85% of the machine's memory); table data 512.0 MB of it (--memory)
  cpu       32 CPUs (the machine's); 16 query threads (half of the CPUs, follows the limit), limits checked every 10 s
  limits    statement timeout 1 min, result memory 1.00 GB, 5000000 groups, 32 statements per user
  safety    writes allowed; DROP TABLE kept 7 days (UNDROP TABLE); audit log off
  logs      slow queries >= 1000 ms to market/_logs/slow_queries.jsonl; pg_stat_statements on; metrics at /metrics
  recovery  manifest copy: a, last shutdown: clean
  open      opened in 2 ms: manifest 0 ms, 1 files read 0 ms (8 table files left for their first use), log check 1 ms, replay 1 ms (0 transactions), clean-up 0 ms

Press Ctrl+C to stop.

Next steps

Syntax:

SQL basics  ->  tables  ->  writing data  ->  queries

Read the topics in this order:

  1. SQL basics: names, text, numbers, NULL and comments.
  2. Databases and CREATE TABLE.
  3. INSERT, UPDATE and DELETE.
  4. SELECT and SAMPLE BY for candles.

Or follow the tutorial chapter by chapter. What's new lists what was added lately.

Example: A taste of time-series SQL: hourly candles of CITY (sample data: sample)

SELECT time, first(open) AS open, max(high) AS high, min(low) AS low, last(close) AS close, sum(volume) AS volume
FROM ochl_minutes
WHERE symbol = 'CITY' AND time IN '2026-09-10'
SAMPLE BY 1h;

Result:

time (TIMESTAMP)open (DOUBLE)high (DOUBLE)low (DOUBLE)close (DOUBLE)volume (DOUBLE)
2026-09-10T05:00:00.000Z504.6507503.8506.436170
2026-09-10T06:00:00.000Z506.4506.6500.7501.241280
2026-09-10T07:00:00.000Z501.2504.1500.7502.646160
2026-09-10T08:00:00.000Z502.6507501.950547560
2026-09-10T09:00:00.000Z505506.1504.3504.911870

(5 rows)