KDB knowledge

SQL basics

How KDB reads SQL text: statements, names, text, numbers, TRUE/FALSE/NULL, comments, errors and reserved words.

SQL is the language you use to talk to KDB. A statement is one command, like SELECT or INSERT. This page shows the small rules that every statement follows.

Example: Names, text, numbers, NULL and a comment in one query

-- the price of one CITY share, and a note
SELECT 'CITY' AS symbol,
       512.45 AS "Last Price",   /* a name with a space needs double quotes */
       100 AS quantity,
       512.45 * 100 AS amount,
       'City''s bank' AS note,
       NULL AS broker;

Result:

symbol (STRING)Last Price (DOUBLE)quantity (LONG)amount (DOUBLE)note (STRING)broker (STRING)
CITY512.4510051245City's bankNULL

(1 row)

Statements and ;

Syntax:

statement ; statement ; ...

A text can hold many statements. Put ; between them. The ; after the last statement is optional. Extra ; are ignored.

Example: Three statements, one result each

CREATE TABLE brokers (id INT PRIMARY KEY, name STRING NOT NULL);
INSERT INTO brokers VALUES (58, 'Northgate Securities');;
SELECT id, name FROM brokers

Result:

CREATE TABLE brokers (

table 'brokers' created

INSERT INTO brokers VALUES (58, 'Northgate Securities')

1 row(s) inserted

SELECT id, name FROM brokers

id (INT)name (STRING)
58Northgate Securities

(1 row)

Example: A script stops at the first error; row 21 stays saved (shows an error)

Setup:

CREATE TABLE brokers (id INT PRIMARY KEY, name STRING NOT NULL);
INSERT INTO brokers VALUES (21, 'Test Broker A');
INSERT INTO brokers VALUES (21, 'Test Broker B');
INSERT INTO brokers VALUES (22, 'Test Broker C');

Result:

1 row(s) inserted

Error: a row with id='21' already exists in 'brokers'. Use INSERT OR REPLACE to overwrite it, or INSERT OR IGNORE to skip it

Example: A syntax error in the second statement: the first does not run either (shows an error)

CREATE TABLE notes (id INT, body STRING);
SELEC * FROM notes;

Result:

Error: line 2, column 1: expected a statement (CREATE, DROP, RENAME, ALTER, SHOW, INSERT, UPDATE, DELETE, BACKUP, CHECK, SELECT, EXPLAIN, CALL, BEGIN, COMMIT, ROLLBACK, SAVEPOINT, DECLARE, FETCH or CLOSE) but found 'SELEC'

Upper and lower case

Syntax:

SELECT = select = Select
ochl_day = OCHL_DAY = Ochl_Day
'CITY' <> 'city'

Keywords, table names, column names and function names do not care about case. Text values do: 'CITY' is not 'city'.

Example: Any case works for names; text compares exactly

CREATE TABLE Brokers (Id INT PRIMARY KEY, Name STRING);
INSERT INTO BROKERS (ID, NAME) VALUES (58, 'Northgate Securities');
select id, name, name = 'northgate securities' AS same_text, upper(name) = 'NORTHGATE SECURITIES' AS same_upper from brokers;

Result:

CREATE TABLE Brokers (

table 'Brokers' created

INSERT INTO BROKERS (ID, NAME) VALUES (58, 'Northgate Securities')

1 row(s) inserted

SELECT id, name, name = 'northgate securities' AS same_text, upper(name) = 'NORTHGATE SECURITIES' AS

Id (INT)Name (STRING)same_text (BOOL)same_upper (BOOL)
58Northgate Securitiesfalsetrue

(1 row)

Names and "quoted names"

Syntax:

name
"any name"
"a name with a "" inside"

A name (also called identifier) names a table, column, alias, view or index. A plain name has letters, digits and _, and does not start with a digit. Letters of any language (Nepali, Chinese, ...) also work.

Write a name in double quotes when it:

PartRule
Plain nameletters (any language), digits, _; not starting with a digit
Quoted nameany characters; a " inside is written twice: ""
Empty name ""not allowed

Example: A forum table with a column called order

CREATE TABLE posts (id LONG PRIMARY KEY, "order" INT, "Posted By" STRING, body STRING);
INSERT INTO posts VALUES (1, 1, 'ram', 'First post');
SELECT id, "order", "Posted By" FROM posts ORDER BY "order";

Result:

CREATE TABLE posts (

table 'posts' created

INSERT INTO posts VALUES (1, 1, 'ram', 'First post')

1 row(s) inserted

SELECT id, "order", "Posted By" FROM posts ORDER BY "order"

id (LONG)order (INT)Posted By (STRING)
11ram

(1 row)

Example: Names in any language work without quotes (here Nepali)

CREATE TABLE कम्पनी (चिन्ह STRING PRIMARY KEY, नाम STRING);
INSERT INTO कम्पनी VALUES ('CITY', 'नमस्ते');
SELECT चिन्ह, नाम FROM कम्पनी;

Result:

CREATE TABLE कम्पनी (

table 'कम्पनी' created

INSERT INTO कम्पनी VALUES ('CITY', 'नमस्ते')

1 row(s) inserted

SELECT चिन्ह, नाम FROM कम्पनी

चिन्ह (STRING)नाम (STRING)
CITYनमस्ते

(1 row)

Example: A reserved word without quotes (shows an error)

CREATE TABLE orders (id INT, order STRING);

Result:

Error: line 1, column 30: expected a column name but found the reserved word 'order' (write it in double quotes, "order", to use it as a name)

Text: 'single quotes'

Syntax:

'text'
'City''s bank'
''

Text (also called a string) goes in single quotes. A ' inside the text is written twice: ''. Text may have many lines and any language (UTF-8).

Example: Quotes inside text, empty text, text in another language

SELECT 'City''s bank' AS name,
       '' AS empty, '' IS NULL AS empty_is_null,
       'नेपाल' AS nepali,
       'a\nb' AS no_escapes;

Result:

name (STRING)empty (STRING)empty_is_null (BOOL)nepali (STRING)no_escapes (STRING)
City's bankfalseनेपालa\nb

(1 row)

Example: Double quotes mean a column name, not text (shows an error)

CREATE TABLE securities (symbol SYMBOL PRIMARY KEY, name STRING);
SELECT name FROM securities WHERE symbol = "CITY";

Result:

table 'securities' created

Error: line 2, column 44: unknown column 'CITY' (table 'securities' has: symbol name)

Example: Text without its closing quote (shows an error)

SELECT 'Northgate;

Result:

Error: line 1, column 8: expected a closing ' to end the string that starts here but found the end of the input

Numbers

Syntax:

42    -7    512.45    .5    1.5e3    2E-4

A number without a point or e is a whole number (type LONG). A number with a point or e is a DOUBLE.

WrittenTypeRange
42, -7LONG-9223372036854775808 to 9223372036854775807
512.45, .5, 1.5e3DOUBLEabout ±1.8e308, 15 digits

Example: Kinds of numbers

SELECT 42 AS whole, -7 AS negative, 512.45 AS price, .5 AS half, 1.5e3 AS thousand_five, 2E-4 AS small, 10 / 4 AS division;

Result:

whole (LONG)negative (LONG)price (DOUBLE)half (DOUBLE)thousand_five (DOUBLE)small (DOUBLE)division (DOUBLE)
42-7512.450.515000.00022.5

(1 row)

Example: A whole number that is too big (shows an error)

SELECT 9223372036854775808;

Result:

Error: line 1, column 8: expected an integer between -9223372036854775808 and 9223372036854775807 but found '9223372036854775808'

TRUE, FALSE and NULL

Syntax:

TRUE    FALSE    NULL

TRUE and FALSE are the two BOOL values. NULL means "no value" or "unknown". Every column type can hold NULL, unless the column is NOT NULL.

Example: The three constants

SELECT TRUE AS yes, FALSE AS no, NULL AS nothing, NULL IS NULL AS is_null, NOT FALSE AS not_false;

Result:

yes (BOOL)no (BOOL)nothing (STRING)is_null (BOOL)not_false (BOOL)
truefalseNULLtruetrue

(1 row)

Example: A user who has not verified the email yet

CREATE TABLE users (id LONG PRIMARY KEY AUTO, email STRING NOT NULL, verified BOOL NOT NULL DEFAULT FALSE, phone STRING);
INSERT INTO users (email) VALUES ('ram@example.com');
INSERT INTO users (email, verified, phone) VALUES ('sita@example.com', TRUE, '9800000000');
SELECT id, email, verified, phone, phone IS NULL AS no_phone FROM users;

Result:

CREATE TABLE users (

table 'users' created

INSERT INTO users (email) VALUES ('ram@example.com')

1 row(s) inserted

INSERT INTO users (email, verified, phone) VALUES ('sita@example.com', TRUE, '9800000000')

1 row(s) inserted

SELECT id, email, verified, phone, phone IS NULL AS no_phone FROM users

id (LONG)email (STRING)verified (BOOL)phone (STRING)no_phone (BOOL)
1ram@example.comfalseNULLtrue
2sita@example.comtrue9800000000false

(2 rows)

Comments

Syntax:

-- to the end of the line
/* between these marks, also over many lines */

KDB skips comments. Use them to explain a script.

Example: Both kinds of comments in a report query

Setup:

CREATE TABLE ledger (id LONG PRIMARY KEY AUTO, account INT, amount DECIMAL(2));
INSERT INTO ledger (account, amount) VALUES (1, 1000.00), (1, -250.50), (2, 250.50);
/* Balance per account.
   Used by the daily report. */
SELECT account,            -- the account number
       sum(amount) AS balance
FROM ledger
GROUP BY account
ORDER BY account;

Result:

account (INT)balance (DECIMAL(2))
1749.50
2250.50

(2 rows)

Example: A comment that is not closed (shows an error)

SELECT 1 /* forgot to close

Result:

Error: line 1, column 10: expected '*/' to close the comment that starts here but found the end of the input

Error messages

Syntax:

error: line L, column C: expected ... but found ...
error: <what is wrong>

When KDB cannot run a statement, it prints error: and a message. Messages about the SQL text say where: line 3, column 8 is the place in the text you sent (in a file: the line of the file). They also say what KDB expected there.

Message starts withMeaning
expected ... but found ...the grammar is wrong at that place
unknown column 'x' (table 't' has: ...)a typing mistake; the message lists the real columns
table or view 'x' does not existwrong table name, or the wrong database
a row with ... already existsa duplicate key; see INSERT
column 'x' (TYPE): ...a value does not fit the column; see column types

Example: The message points to line 3, column 8 (shows an error)

Setup:

CREATE TABLE ochl_day (time TIMESTAMP, symbol SYMBOL, close DOUBLE) TIMESERIES(time);
SELECT time,
       symbol,
       clsoe
FROM ochl_day;

Result:

Error: line 3, column 8: unknown column 'clsoe' (table 'ochl_day' has: time symbol close)

Example: What the grammar wanted (shows an error)

Setup:

CREATE TABLE ochl_day (time TIMESTAMP, symbol SYMBOL, close DOUBLE) TIMESERIES(time);
SELECT symbol close FROM ochl_day WHERE;

Result:

Error: line 1, column 40: expected an expression (a value, column name, function call or '(') but found ';'

Reserved words

Syntax:

ALTER AND AS BETWEEN CASE CREATE DEFAULT DELETE DISTINCT DROP ELSE END
FALSE FROM GROUP IN INSERT INTO IS JOIN LIKE LIMIT NOT NULL ON OR ORDER
PRIMARY REFERENCES SELECT SET TABLE THEN TO TRUE UNIQUE UPDATE VALUES WHEN WHERE

These 40 words have a fixed meaning. They cannot be a table, column or alias name, unless you write them in double quotes: "order", "from".

Example: Columns called time, date, open, close, key and value need no quotes

CREATE TABLE prices (time TIMESTAMP, date DATE, open DOUBLE, close DOUBLE, key STRING, value DOUBLE) TIMESERIES(time);
INSERT INTO prices VALUES ('2026-09-10', '2026-09-10', 504.6, 504.9, 'CITY', 1);
SELECT time, date, open, close, key, value FROM prices;

Result:

CREATE TABLE prices (

table 'prices' created

INSERT INTO prices VALUES ('2026-09-10', '2026-09-10', 504.6, 504.9, 'CITY', 1)

1 row(s) inserted

SELECT time, date, open, close, key, value FROM prices

time (TIMESTAMP)date (DATE)open (DOUBLE)close (DOUBLE)key (STRING)value (DOUBLE)
2026-09-10T00:00:00.000Z2026-09-10504.6504.9CITY1

(1 row)

Example: A reserved word as a column name (shows an error)

CREATE TABLE transfers (id INT, from INT, to_account INT);

Result:

Error: line 1, column 33: expected a column name but found the reserved word 'from' (write it in double quotes, "from", to use it as a name)

Clause words

Syntax:

ASOF CROSS FULL HAVING INNER LATEST LEFT LT OVER REFRESH RIGHT SAMPLE SPLICE UNION USING

These 15 words start a part of a query (a clause), like LEFT JOIN or SAMPLE BY. They can be table and column names. But they cannot be a short alias without AS: write SELECT 1 AS left, not SELECT 1 left.

WordUsed in
INNER, LEFT, RIGHT, FULL, CROSS, USINGjoins
ASOF, LT, SPLICEtime joins
HAVINGGROUP BY
LATESTLATEST ON
SAMPLESAMPLE BY
UNIONUNION ALL
OVERwindow functions
REFRESHCREATE CACHED VIEW ... REFRESH ON COMMIT (views)

Example: Clause words as column names, and as aliases with AS

CREATE TABLE t (left INT, right INT, sample INT, latest INT);
INSERT INTO t VALUES (1, 2, 3, 4);
SELECT left, right, sample AS union, latest AS over FROM t;

Result:

CREATE TABLE t (

table 't' created

INSERT INTO t VALUES (1, 2, 3, 4)

1 row(s) inserted

SELECT left, right, sample AS union, latest AS over FROM t

left (INT)right (INT)union (INT)over (INT)
1234

(1 row)

Example: RIGHT JOIN: every broker, also the ones without trades

Setup:

CREATE TABLE brokers (id INT PRIMARY KEY, name STRING);
CREATE TABLE trades (id LONG PRIMARY KEY, buyer INT);
INSERT INTO brokers VALUES (1, 'Alpha'), (2, 'Beta');
INSERT INTO trades VALUES (10, 1);
SELECT brokers.name, trades.id FROM trades RIGHT JOIN brokers ON trades.buyer = brokers.id ORDER BY brokers.name;

Result:

name (STRING)id (LONG)
Alpha10
BetaNULL

(2 rows)

Example: A clause word as a short alias (shows an error)

SELECT 1 left;

Result:

Error: line 1, column 10: expected an operator, AS, ',', FROM, WHERE, GROUP BY, UNION ALL, ORDER BY, LIMIT, ';' or the end of the input but found 'left'

NULL logic

Syntax:

value IS NULL
value IS NOT NULL
NULL = anything   ->  NULL (unknown)

NULL means "unknown". So most things done with NULL give NULL again:

ExpressionResultWhy
NULL + 1NULLunknown plus 1 is unknown
NULL = NULLNULLtwo unknown values may differ
TRUE AND NULLNULLdepends on the unknown value
FALSE AND NULLFALSEfalse whatever the unknown value is
TRUE OR NULLTRUEtrue whatever the unknown value is
NOT NULLNULL
3 IN (1, NULL)NULL3 might be the unknown value
NULL IS NULLTRUEthe right way to test

Example: NULL in expressions

SELECT NULL + 1 AS plus, NULL = NULL AS equal, TRUE AND NULL AS and_true, FALSE AND NULL AS and_false,
       TRUE OR NULL AS or_true, 3 IN (1, NULL) AS in_list, NULL IS NULL AS is_null;

Result:

plus (STRING)equal (STRING)and_true (STRING)and_false (BOOL)or_true (BOOL)in_list (STRING)is_null (BOOL)
NULLNULLNULLfalsetrueNULLtrue

(1 row)

Example: Users without a phone: = NULL finds nothing, IS NULL works

Setup:

CREATE TABLE users (id INT PRIMARY KEY, email STRING, phone STRING);
INSERT INTO users VALUES (1, 'ram@example.com', NULL), (2, 'sita@example.com', '9800000000'), (3, 'hari@example.com', NULL);
SELECT id FROM users WHERE phone = NULL;
SELECT id FROM users WHERE phone IS NULL;
SELECT id FROM users WHERE phone <> '9800000000';
SELECT count(*) AS users, count(phone) AS with_phone FROM users;

Result:

SELECT id FROM users WHERE phone = NULL

id (INT)

(0 rows)

SELECT id FROM users WHERE phone IS NULL

id (INT)
1
3

(2 rows)

SELECT id FROM users WHERE phone != '9800000000'

id (INT)

(0 rows)

SELECT count(*) AS users, count(phone) AS with_phone FROM users

users (LONG)with_phone (LONG)
31

(1 row)