# 11. SQL reference - Category: Tutorial ## What you will learn - The exact form of every KDB statement. - Every type, function and operator, with a short example. - The time rules, reserved words and limits. This is a short reference. Chapters 3 to 5 explain everything with longer examples. In the syntax, `[x]` means "x is optional", `a | b` means "a or b", and `...` means "more of the same". The example results in this chapter are real. They come from `examples/11-sql-reference/`. ## General rules | Rule | Detail | |---|---| | Statements | End with `;`. Many statements can be sent at once. Each one is saved on its own (statements between `BEGIN` and `COMMIT`, and a whole `CALL`, are saved together); KDB stops at the first error. | | Case | Keywords, table names, column names and function names do not care about case. Text values do (`'CITY'` is not `'city'`). | | Names | Letters, digits, `_`, and non-English letters. A reserved word or a special name must be in double quotes: `"order"`. | | Text | In single quotes: `'CITY'`. A quote inside is doubled: `'City''s'`. | | Numbers | `42`, `-7`, `512.45`, `1.5e3`. Integers from -9223372036854775808 to 9223372036854775807. | | Other constants | `TRUE`, `FALSE`, `NULL` | | Comments | `-- to the end of the line` and `/* ... */` | | Errors | Say where: `line 1, column 22: ...` | ## Statements ### Tables ```sql CREATE TABLE [IF NOT EXISTS] name ( column TYPE [PRIMARY KEY [AUTO]] [NOT NULL] [[CONSTRAINT name] UNIQUE] [DEFAULT expr] [[CONSTRAINT name] REFERENCES table[(column)] [ON DELETE action] [ON UPDATE action]] [ROUND interval] [AS (expr)] [[CONSTRAINT name] CHECK (condition)], ... [, [CONSTRAINT name] CHECK (condition) ...] [, [CONSTRAINT name] PRIMARY KEY (column, ...)] [, [CONSTRAINT name] UNIQUE (column, ...)] [, [CONSTRAINT name] FOREIGN KEY (column, ...) REFERENCES table (column, ...) [ON DELETE action] [ON UPDATE action]] ) [TIMESERIES(time_column)] [PARTITION BY DAY | MONTH | YEAR | NONE] [SORT BY (column, ...)] [DEDUP KEY (column, ...)] [TTL n HOURS | DAYS | WEEKS | MONTHS | YEARS] [RECORDS]; DROP TABLE [IF EXISTS] name; TRUNCATE [TABLE] name [, name ...]; -- removes every row at once; not inside BEGIN ... COMMIT RENAME TABLE old TO new; ALTER TABLE name ADD [COLUMN] column TYPE [rules]; ALTER TABLE name DROP [COLUMN] column; ALTER TABLE name RENAME [COLUMN] old TO new; ALTER TABLE name DROP PARTITION 'key' [, 'key' ...]; ALTER TABLE name DROP PARTITION OLDER THAN n DAYS | MONTHS | YEARS | ...; ALTER TABLE name SET TTL n DAYS | ... | NONE; ALTER TABLE name DEDUP ENABLE UPSERT KEYS (column, ...); -- or DEDUP DISABLE; rewrites the rows ALTER TABLE name ADD [CONSTRAINT cname] CHECK (condition); ALTER TABLE name DROP CONSTRAINT cname; -- PostgreSQL forms (chapter 22): CREATE TABLE name AS SELECT ...; ALTER TABLE [IF EXISTS] name action [, action ...]; -- ADD [COLUMN] [IF NOT EXISTS] ..., DROP [COLUMN] [IF EXISTS] c [CASCADE], -- ALTER [COLUMN] c {SET DEFAULT expr | DROP DEFAULT | SET NOT NULL | DROP NOT NULL | TYPE type [USING expr]}, -- ADD [CONSTRAINT n] {UNIQUE (...) | PRIMARY KEY (...) | FOREIGN KEY (...) REFERENCES ... | CHECK (...)} [NOT VALID], -- DROP CONSTRAINT [IF EXISTS] n, RENAME [COLUMN] a TO b, RENAME TO new SHOW TABLES; SHOW COLUMNS FROM name; SHOW CREATE TABLE name; SHOW VIEWS; ``` | Part | Rules | |---|---| | `TIMESERIES(col)` | Makes a time-series table. `col` is TIMESTAMP or DATE. | | `PARTITION BY` | Time-series only. Default `DAY`. Partition keys: `'2026'`, `'2026-09'`, `'2026-09-10'` (UTC). | | `SORT BY` | Time-series only. Default: first SYMBOL column, then the time column. | | `DEDUP KEY` | Time-series only. Must include the time column. | | `TTL n DAYS` | Time-series only. Partitions whose last moment is older than that are dropped by themselves. `SET TTL NONE` stops it. `DROP PARTITION OLDER THAN` does the same once, now. | | `TRUNCATE` | Removes every row, and keeps the table, its columns and indexes. Much faster than `DELETE FROM t`. Returns `1 table(s) truncated (57 row(s) removed)`. `ROLLBACK` cannot undo it, so it is refused inside `BEGIN` ... `COMMIT`. Needs the `write` role (`TRUNCATE ALL TABLES`: `admin`). | | `RECORDS` | Says "relational table" explicitly. Cannot be used with TIMESERIES. | | `PRIMARY KEY` | At most one per table. Implies NOT NULL. | | `AUTO` | Integer PRIMARY KEY only. Next value from the table's counter, which only goes up (never reuses ids of deleted or rolled-back rows). | | `DEFAULT` | A constant or `now()`. No column names. | | `AS (expr)` | Computed column, not stored. Uses stored columns of the same row only. No DEFAULT, keys or REFERENCES. Cannot be set by INSERT/UPDATE. | | `REFERENCES t(c)` | `t.c` must be PRIMARY KEY or UNIQUE and of the same kind of type. Checked on INSERT and UPDATE. Without `ON DELETE`, a referenced row cannot be deleted; a referenced table cannot be dropped. `REFERENCES t` means t's PRIMARY KEY. | | `ON DELETE` / `ON UPDATE` | `NO ACTION` (default), `RESTRICT`, `CASCADE`, `SET NULL`, `SET DEFAULT`. All changes of a statement are one transaction. Chapter 22. | | `PRIMARY KEY (a, b)` | Rows are stored by the key. | | PostgreSQL type names | `SERIAL`, `VARCHAR(n)`, `NUMERIC(p,s)`, `UUID`, `TEXT[]`, ... See [Types](#types) and chapter 22. | | `ROUND interval` | TIMESTAMP only. Cuts values down: `ROUND 1m`, `ROUND 1d`. | | `CHECK (condition)` | Checked for every written row (INSERT, UPDATE, upserts, `InsertRows`, line protocol). A row where it is false is refused; NULL passes. Only stored columns of the same row and built-in functions. Name when not given: `table_column_check` / `table_check`. `ADD CHECK` refuses when a row already breaks it. Chapter 15. | | `ADD COLUMN ... NOT NULL` | Needs a DEFAULT. | | `DROP COLUMN` | Not for: the TIMESERIES column, SORT BY / DEDUP KEY columns, the PRIMARY KEY, referenced columns, columns used by computed columns or indexes, the only column. | ### Writing ```sql [WITH name AS (query), ...] INSERT [OR REPLACE | OR IGNORE] INTO table [AS alias] [(column, ...)] VALUES (value, ...) [, (value, ...) ...] | SELECT ... | DEFAULT VALUES [ON CONFLICT [(column, ...) [WHERE ...] | ON CONSTRAINT name] DO NOTHING | DO UPDATE SET column = expr, ... [WHERE condition]] -- chapter 23 [RETURNING expr [[AS] alias], ... | *]; UPDATE table [[AS] alias] SET column = expr [, column = expr ...] [FROM source, ...] [WHERE condition] [RETURNING ...]; DELETE FROM table [[AS] alias] [USING source, ...] [WHERE condition] [RETURNING ...]; ``` - `RETURNING` gives rows like a SELECT: the inserted rows (with AUTO ids and defaults), the rows after an UPDATE, the deleted rows. - Values are constants or expressions without columns (`now()`, `1 + 1`, `CAST(...)`), and subqueries. - `ON CONFLICT`, `UPDATE ... FROM`, `DELETE ... USING`, `WITH x AS (UPDATE ... RETURNING ...)` and subqueries (also correlated ones) in `SET` and `WHERE`: chapter 23. - There is no `MERGE`. To empty a table, use `TRUNCATE` (see [Tables](#tables)). ### Queries ```sql [WITH [RECURSIVE] name [(column, ...)] AS (query), ...] SELECT [DISTINCT | DISTINCT ON (expr, ...)] expr [[AS] alias], ... | * | table.* [FROM table | view | (SELECT ...) [[AS] alias [(column, ...)]] | (VALUES (...), ...) AS alias (column, ...) | unnest(array) [AS alias (column)] | [LEFT JOIN] LATERAL (SELECT ...) alias [[INNER] JOIN | LEFT [OUTER] JOIN source ON condition] [ASOF JOIN source [ON condition]] [CROSS JOIN source | , source] ...] [WHERE condition] [LATEST ON time_column PARTITION BY column, ...] [SAMPLE BY interval [FILL(NONE | NULL | PREV | LINEAR | constant)] [ALIGN TO CALENDAR | ALIGN TO FIRST OBSERVATION] [TIME ZONE 'zone'] [WITH OFFSET '+HH:MM'] [WEEK START SUNDAY | MONDAY]] [GROUP BY expr, ...] [HAVING condition] [UNION [ALL] | INTERSECT [ALL] | EXCEPT [ALL] SELECT ... ...] [ORDER BY expr [ASC | DESC] [NULLS FIRST | NULLS LAST], ...] [LIMIT n | ALL] [OFFSET m [ROWS]] [FETCH FIRST n ROWS ONLY] [FOR UPDATE]; -- locks the tables it reads until the transaction ends (chapter 15) EXPLAIN SELECT ...; ``` | Rule | Detail | |---|---| | Implicit GROUP BY | With aggregates or SAMPLE BY and no GROUP BY, every select item without an aggregate is a group key. | | SAMPLE BY | Time-series tables (or subqueries with a TIMESTAMP column). The output time is the bucket start. | | SAMPLE BY intervals | `ms`, `s`, `m`, `h`, `d`, `w`, `M` (month), `y` (year). `m` = minute, `M` = month. | | WEEK START | Only with a week interval. Default Monday. | | ALIGN TO FIRST OBSERVATION | Not with TIME ZONE, WITH OFFSET, months or years. | | FILL | Applies to all aggregate columns. Fills only between the first and last bucket with data. At most 10 million rows. | | LATEST ON | One table, no joins. Runs after WHERE. | | ASOF JOIN | Both sides need a time column. Takes the last right row at or before the left row's time. | | ORDER BY | Output column name, alias, position (`ORDER BY 2`), or an expression. Default NULL position: NULLS LAST for ASC, NULLS FIRST for DESC. | | UNION, INTERSECT, EXCEPT | Same number of columns. Without `ALL` the rows are distinct. `INTERSECT` goes before `UNION` and `EXCEPT`. ORDER BY / LIMIT at the end apply to all rows and must use output names or positions. | | Subqueries | `(SELECT ...)` as a value (must return 0 or 1 row), `IN (SELECT ...)`, `EXISTS (SELECT ...)`, `FROM (SELECT ...)`, `LATERAL`. They may use outer columns (correlated); `inner.col = outer.col` becomes a hash lookup (chapter 23). | | Aggregates | `agg(x) FILTER (WHERE cond)`, `array_agg(x ORDER BY y)`, `GROUP BY 1` (a select column), aggregates in ORDER BY and HAVING. | | Window functions | With GROUP BY or SAMPLE BY they run over the groups, one row per group. Their arguments must be aggregates or group keys: `sma(last(close), 2)`, `rank() OVER (ORDER BY sum(volume) DESC)`. `sma(close, 2)` there is an error (`column 'close' is used by a window function in a query with aggregates, so it must be a key of the groups`). Not inside each other (`lag(sma(close, 3))`): use a subquery. | | Big GROUP BY | When the groups need more than half of `--max-result-memory`, KDB writes them to temporary files (`_tmp/` in the database folder) and adds them up after the scan. The result is the same, only slower. At most 16 GB of such files per query. | | `SELECT` without `FROM` | Allowed: `SELECT 1 + 1`. | ### Views ```sql CREATE [OR REPLACE] [CACHED] VIEW [IF NOT EXISTS] name AS [(] SELECT ... [)] [REFRESH ON COMMIT]; ALTER VIEW name AS SELECT ...; REFRESH VIEW name; DROP VIEW [IF EXISTS] name; CREATE MATERIALIZED VIEW [IF NOT EXISTS] name [PARTITION BY DAY | MONTH | YEAR] [TTL n DAYS] AS SELECT time_bucket, ..., aggregates FROM table GROUP BY time_bucket, ...; REFRESH MATERIALIZED VIEW name [FOR 'time' [TO 'time']]; DROP MATERIALIZED VIEW [IF EXISTS] name; SHOW VIEWS; ``` | Kind | What it keeps | |---|---| | View | Nothing. Its query runs every time you use it. | | `CACHED` view | Its rows, in memory. Made at `CREATE` and at `REFRESH VIEW`; with `REFRESH ON COMMIT` again at the first read after a commit changed one of its tables. `CREATE` says `view 'name' created (4 row(s) cached)`. | | `MATERIALIZED` view | A real table with the result of a `GROUP BY` over one time-series table. After each commit, a background thread rebuilds only the partitions that changed. The `GROUP BY` must have a time bucket: the time column, `to_date(time)` or `date_trunc('day', time)`. | `REFRESH ON COMMIT` needs `CACHED`. A view and a table cannot have the same name. ### Indexes and full-text search Chapter 17 explains them with a forum and a news example. ```sql CREATE [UNIQUE | FULLTEXT] INDEX [IF NOT EXISTS] name ON table (column, ...); CREATE [UNIQUE] INDEX [CONCURRENTLY] [IF NOT EXISTS] [name] ON [ONLY] table [USING method] ({column | (expression)} [DESC] [NULLS LAST], ...) [WHERE condition]; -- PostgreSQL form, chapter 22 DROP INDEX [IF EXISTS] name; SHOW INDEXES [FROM table]; -- columns: name, table, kind, columns, built, rows, memory_bytes, create_sql ``` | Rule | Detail | |---|---| | Name | Unique in the database. | | Columns | 1 to 16 stored columns (not computed). Relational and time-series tables. | | `UNIQUE` | Two rows cannot have the same values. NULL values never clash. Error: `duplicate value violates unique index 'name' on table 't': a row with col='value' already exists`. Creating it fails when rows already clash. | | `FULLTEXT` | STRING, SYMBOL or JSON columns. Used by `MATCH(...)`, `search_rank(...)`. | | Where the data lives | In memory only. `kdb serve` builds it from the rows right after the database opens, on a background thread. In `kdb sql`, `kdb shell` and with `--warm-indexes off`, the first query that needs it builds it. `CREATE INDEX` builds it at once. Nothing on disk: crashes, `kdb check`, `kdb repair`, backups need nothing extra. | | When a query uses an index | `col = value`, `col IN (...)`, `col IS NULL` on the first columns, then a range (`<`, `<=`, `>`, `>=`, `BETWEEN`, `LIKE 'abc%'`, `time IN '...'`, a row comparison) on the next column. `ORDER BY` the next columns (all ASC or all DESC) with `LIMIT` reads only the rows needed. `MATCH` uses a FULLTEXT index on the same columns. `EXPLAIN` shows `use index name (...)`. Also used by `UPDATE` and `DELETE`. | | When it does not | A condition with `OR`, a function around the column, `!=`, `NOT LIKE`, a FLOAT column, text compared with a number, or when the index would read more than a quarter of the table. | | `DROP COLUMN` | Refused while an index uses the column. `RENAME COLUMN` / `RENAME TABLE` keep the index. `DROP TABLE` drops its indexes. | | Expression and partial | `(lower(email))`, `WHERE is_default`. A UNIQUE one is checked on every write. A non-unique one is used by a query that asks for the same thing: `lower(email) = 'a@x.np'`, or a `WHERE` that has every part of the index's own `WHERE`. | | Not in a transaction | `CREATE INDEX` and `DROP INDEX` cannot be used between `BEGIN` and `COMMIT`. | ### Sequences Chapter 22 explains them. ```sql CREATE SEQUENCE [IF NOT EXISTS] name [INCREMENT [BY] n] [MINVALUE n] [MAXVALUE n] [START [WITH] n] [[NO] CYCLE]; ALTER SEQUENCE name [RESTART [WITH n]] [INCREMENT [BY] n] ...; DROP SEQUENCE [IF EXISTS] name [, ...]; SELECT nextval('name'), currval('name'), setval('name', n [, is_called]), lastval(); ``` A number is never given twice, also after a crash. `currval` and `lastval` are per session. ### PostgreSQL schema statements `CREATE SCHEMA`, `CREATE EXTENSION` (pgcrypto, uuid-ossp, citext, plpgsql, pg_trgm, btree_gin, btree_gist, unaccent) and `COMMENT ON` are accepted and change nothing. `DO $$ BEGIN ... END $$` runs `IF`, `RAISE` and SQL statements. The views `information_schema.tables`, `.columns`, `.table_constraints`, `.key_column_usage`, ... and `pg_class`, `pg_attribute`, `pg_indexes`, `pg_constraint`, ... describe the database. Chapter 22. ### Functions, procedures and triggers Chapter 13 explains them with examples. ```sql CREATE [OR REPLACE] FUNCTION name(param TYPE, ...) [RETURNS TYPE] AS (expression); DROP FUNCTION [IF EXISTS] name; SHOW FUNCTIONS; -- columns: name, arguments, returns, definition CREATE [OR REPLACE] PROCEDURE name(param TYPE, ...) AS BEGIN statement; ... END; CALL name(argument, ...) [WITH DEBUG]; DROP PROCEDURE [IF EXISTS] name; SHOW PROCEDURES; -- columns: name, arguments, definition CREATE [OR REPLACE] TRIGGER name {BEFORE | AFTER} {INSERT | UPDATE | DELETE} ON table EXECUTE {CALL procedure(argument, ...) | INSERT ... | UPDATE ... | DELETE ... | BEGIN statement; ... END}; ALTER TABLE table {ENABLE | DISABLE} TRIGGER name; DROP TRIGGER [IF EXISTS] name; CREATE [OR REPLACE] TRIGGER name AFTER INSERT ON table DEFERRED [TIMEOUT 10min] EXECUTE ...; RETRY TRIGGER QUEUE name; -- a DEFERRED trigger: run the failed job again SKIP TRIGGER JOB name job_id; -- a DEFERRED trigger: leave one job out SHOW TRIGGERS; -- columns: name, table, timing, event, enabled, definition ``` Statements inside a procedure or trigger body (each ends with `;`): ```sql SELECT ... | INSERT ... | UPDATE ... | DELETE ... | CALL name(...) | SHOW ... | EXPLAIN ... DECLARE variable TYPE [DEFAULT expr] SET variable = expr -- also: SET variable = (SELECT ...) IF condition THEN ... [ELSIF condition THEN ...] [ELSE ...] END IF WHILE condition DO ... END WHILE FOR row IN (SELECT ...) DO ... END FOR -- row.column is a value of the current row DECLARE cursor CURSOR FOR SELECT ... OPEN cursor FETCH cursor INTO variable, ... -- sets the variable FOUND CLOSE cursor RETURN RAISE expr -- error: the whole CALL (or statement) is undone PRINT expr -- a line in the "print" (or "debug") result ``` | Rule | Detail | |---|---| | Function body | One expression using only its parameters. No subqueries, aggregates or window functions. Not in computed columns. The name cannot be a built-in function. | | Names in a body | A column name wins over a variable or parameter with the same name. | | `CALL` | One transaction. Its SELECTs return extra results; `PRINT` adds a `print` result; `WITH DEBUG` adds a `debug` result (step, routine, statement, rows, time_us, changes). | | Triggers | Once per statement (also once per `InsertRows` call and `POST /write` request), in the same transaction. The body reads the tables `NEW` and `OLD`. | | `DEFERRED` | Only `AFTER INSERT`. Runs after the commit, as a background job of the server; the insert does not wait. Chapter 13. | | `DROP TABLE`, `RENAME TABLE` | Refused while the table has triggers. | ### Transactions and cursors ```sql BEGIN [TRANSACTION | WORK]; COMMIT [TRANSACTION | WORK]; ROLLBACK [TRANSACTION | WORK]; DECLARE name CURSOR FOR SELECT ...; -- runs the query and keeps the rows FETCH [NEXT [n] | ALL] FROM name; -- NEXT without n = 1 row CLOSE name; ``` | Rule | Detail | |---|---| | Scope | A transaction and a cursor end with the text they were sent in (`kdb sql`, `/exec`, the web console). In a session (`kdb shell`, `POST /session` + `X-KDB-Session`, a PostgreSQL connection) they stay open over many requests. A text without a session that ends without `COMMIT` is rolled back with an error. | | Errors | A failing statement rolls back the whole open transaction. In a session, only `ROLLBACK` (or `COMMIT`, which then fails) is accepted after that. | | Many writers | Writers of different tables run at the same time; writers of the same table take turns: they wait up to 5 s (`table 'x' is busy`), and two transactions that would wait for each other get `deadlock` at once. Readers see the committed rows. An idle transaction is rolled back after 30 s. Chapter 15. | | Not in a transaction | `CREATE`, `DROP`, `ALTER`, `RENAME`, `TRUNCATE`, `BACKUP DATABASE`. | | HTTP | `POST /cursor`, `GET /cursor/?fetch=N`, `DELETE /cursor/` (chapter 8). | ### Database ```sql BACKUP DATABASE; -- needs --backups on kdb serve / shell / sql CHECK TABLE any_name; -- reads every file of the whole database ``` `CHECK TABLE` checks **all** files; the table name is not used. ### Databases A **database** is a folder in the **data folder** (chapter 1). Each database has its own tables, views, triggers and procedures. ```sql CREATE DATABASE [IF NOT EXISTS] name; DROP DATABASE [IF EXISTS] name; -- closes it and moves its folder to /_trash SHOW DATABASES; -- columns: name, tables, bytes, current USE name; -- the statements after it use this database SELECT ... FROM other_db.table ...; -- read a table of another database on the same server ``` | Rule | Detail | |---|---| | Names | Letters, digits, `_` and `-`, 1 to 64 characters. `Market` and `market` are the same database. The folder keeps the name as you first wrote it. Not allowed: `_trash` and the Windows device names `CON`, `PRN`, `AUX`, `NUL`, `COM0`-`COM9`, `LPT0`-`LPT9`. | | `DROP DATABASE` | Refused while another request or shell uses the database. Nothing is deleted: the folder moves to `_trash/