SELECT
Choose the columns and values that a query gives back, and the table they come from.
- Category: Querying
- Keywords: query, read rows, columns, *, table.*, AS, alias, column name, FROM, DISTINCT, unique rows, SELECT without FROM, calculator, missing comma, select, from, distinct
- See also: WHERE, ORDER BY, LIMIT, OFFSET, GROUP BY and HAVING, Joins, UNION and subqueries, Views
SELECT reads data. You say which values you want (the select list), and from which table (FROM).
Every other part (WHERE, GROUP BY, ORDER BY, ...) is optional and has its own help page.
A select item can be a column, a calculation, a function or a constant. Give it a name with AS.
Example: Today's change of every symbol, like a market summary (sample data: sample)
SELECT symbol, open, close,
round(close - open, 2) AS change,
round((close - open) / open * 100, 2) AS change_pct
FROM ochl_day
WHERE time IN '2026-09-10'
ORDER BY change_pct DESC;
Result:
| symbol (SYMBOL) | open (DOUBLE) | close (DOUBLE) | change (DOUBLE) | change_pct (DOUBLE) |
|---|---|---|---|---|
| VOLT | 185.6 | 191.2 | 5.6 | 3.02 |
| CITY | 504.6 | 504.9 | 0.3 | 0.06 |
| COAST | 380.1 | 376.1 | -4 | -1.05 |
(3 rows)
SELECT
Syntax:
SELECT [DISTINCT] item [[AS] alias], ...
[FROM source [JOIN ...]]
[WHERE condition]
[LATEST ON time PARTITION BY column, ...]
[SAMPLE BY interval ...]
[GROUP BY expression, ...]
[HAVING condition]
[UNION ALL SELECT ...]
[ORDER BY expression [ASC | DESC], ...]
[LIMIT n [OFFSET m]]
The parts must be written in this order. KDB does the work in another order:
| Step | Part | What happens |
|---|---|---|
| 1 | FROM, JOIN | read the tables |
| 2 | WHERE | keep only the rows that match |
| 3 | LATEST ON | keep the newest row of each group |
| 4 | SAMPLE BY, GROUP BY | make groups and compute sum, max, ... |
| 5 | HAVING | keep only the groups that match |
| 6 | window functions | lag, sma, row_number, ... |
| 7 | select list, DISTINCT | compute the output columns |
| 8 | UNION ALL | add the rows of the next query |
| 9 | ORDER BY, LIMIT, OFFSET | sort and cut |
| Item | Example |
|---|---|
| a column | close |
| a column of one table (with joins) | f.symbol |
| a calculation | high - low |
| a function | round(close, 1), upper(name) |
| an aggregate | count(*), max(high) (see GROUP BY) |
| a constant | 'hello', 100, TRUE, NULL |
| all columns | *, f.* |
- Returns: a table of rows. The column names come from the aliases, else from the items.
- Notes: names of tables, columns and functions do not care about case (
CLOSEisclose). Text values do:'CITY'is not'city'.
Example: Trade log: the value of each trade on 2026-09-09 (sample data: sample)
SELECT transaction_id, symbol, quantity, rate, quantity * rate AS value
FROM trade_log
WHERE date IN '2026-09-09'
ORDER BY value DESC
LIMIT 4;
Result:
| transaction_id (LONG) | symbol (SYMBOL) | quantity (LONG) | rate (DOUBLE) | value (DOUBLE) |
|---|---|---|---|---|
| 100001 | TELCO | 1000 | 881 | 881000 |
| 100007 | TELCO | 1000 | 879 | 879000 |
| 100005 | CITY | 500 | 517 | 258500 |
| 100004 | CITY | 500 | 514.5 | 257250 |
(4 rows)
Example: App users: name, email and plan (sample data: query-app)
SELECT username, lower(email) AS email, upper(plan) AS plan
FROM app_users
WHERE id <= 4;
Result:
| username (STRING) | email (STRING) | plan (STRING) |
|---|---|---|
| ram | ram.sharma@gmail.com | PRO |
| sita | sita.k@yahoo.com | FREE |
| hari_99 | hari99@gmail.com | FREE |
| gita | gita.rai@gmail.com | PRO |
(4 rows)
Example: Accounting: the amount of each journal line, debit or credit (sample data: query-app)
SELECT line_id, entry_id, account, coalesce(debit, 0) - coalesce(credit, 0) AS amount, memo
FROM ledger
WHERE entry_id IN (2, 8);
Result:
| line_id (INT) | entry_id (INT) | account (INT) | amount (DECIMAL(2)) | memo (STRING) |
|---|---|---|---|---|
| 3 | 2 | 1200 | 257500.00 | Buy 500 CITY at 515 |
| 4 | 2 | 2000 | -257500.00 | Buy 500 CITY at 515 |
| 15 | 8 | 1100 | 12600.00 | Sell 20 CITY at 630 |
| 16 | 8 | 1200 | -10300.00 | Sell 20 CITY at 630 |
| 17 | 8 | 4100 | -2300.00 | Sell 20 CITY at 630 |
(5 rows)
, table.
Syntax:
SELECT * FROM table
SELECT alias.*, other_item, ... FROM table alias JOIN ...
* means every column of every table in FROM, in table order. alias.* means every column of one table.
- Returns: the stored columns and the computed columns (
AS (...)) of the table. - Notes:
*cannot be used together with aggregates orSAMPLE BY. In programs, list the columns you need: KDB then takes only those columns out of each stored row, which is faster on wide tables (EXPLAINshowsbuild 3 of 7 stored column(s) per row), and a new column in the table does not change your result.
Example (sample data: sample)
SELECT * FROM brokers;
Result:
| id (INT) | name (STRING) |
|---|---|
| 1 | Keystone Securities |
| 34 | Vision Securities |
| 45 | Imperial Securities |
| 49 | Online Securities |
| 58 | Northgate Securities |
(5 rows)
Example: All columns of the trade, plus the buyer's name (sample data: sample)
SELECT f.*, b.name AS buyer_name
FROM trade_log f
JOIN brokers b ON b.id = f.buyer
WHERE f.transaction_id = 100017;
Result:
| date (TIMESTAMP) | transaction_id (LONG) | symbol (SYMBOL) | buyer (INT) | seller (INT) | quantity (LONG) | rate (DOUBLE) | amount (DOUBLE) | buyer_name (STRING) |
|---|---|---|---|---|---|---|---|---|
| 2026-09-10T00:00:00.000Z | 100017 | TELCO | 49 | 34 | 500 | 881 | 440500 | Online Securities |
(1 row)
Example: * and count(*) do not mix (sample data: sample; shows an error)
SELECT *, count(*) FROM brokers;
Result:
Error: line 1, column 8: * cannot be combined with aggregates or SAMPLE BY
AS: column names
Syntax:
item AS alias
item alias
item AS "any name"
AS gives an output column a name. The word AS is optional.
| Written | Column name |
|---|---|
close | close |
close AS last_price | last_price |
close * 2 (no alias) | close * 2 |
count(*) (no alias) | count(*) |
close AS "Close Price" | Close Price (double quotes keep spaces and case) |
- Notes: a missing comma is not an error:
SELECT symbol closegives one column namedclosethat holds the symbol. Check your commas. - A reserved word (like
order,select,from) needs double quotes:AS "order". - The words
ASOF,CROSS,FULL,HAVING,INNER,LATEST,LEFT,LT,OVER,REFRESH,RIGHT,SAMPLE,SPLICE,UNIONandUSINGneedASto be an alias:close AS leftworks,close leftdoes not. - An alias can be used in
ORDER BY, but not inWHERE,GROUP BYorHAVING(write the expression again there). See ORDER BY and GROUP BY.
Example (sample data: sample)
SELECT symbol, close AS last_price, close last, close * 2, round(close) AS "Close (whole dollars)"
FROM ochl_day
WHERE time IN '2026-09-10';
Result:
| symbol (SYMBOL) | last_price (DOUBLE) | last (DOUBLE) | close * 2 (DOUBLE) | Close (whole dollars) (DOUBLE) |
|---|---|---|---|---|
| CITY | 504.9 | 504.9 | 1009.8 | 505 |
| COAST | 376.1 | 376.1 | 752.2 | 376 |
| VOLT | 191.2 | 191.2 | 382.4 | 191 |
(3 rows)
Example: Aggregates without an alias get their text as the name (sample data: sample)
SELECT symbol, count(*), max(high) AS highest
FROM ochl_day
GROUP BY symbol;
Result:
| symbol (SYMBOL) | count(*) (LONG) | highest (DOUBLE) |
|---|---|---|
| CITY | 19 | 546.2 |
| COAST | 19 | 429.9 |
| VOLT | 19 | 199.7 |
(3 rows)
Example: A missing comma quietly renames the column (sample data: sample)
SELECT symbol close FROM securities LIMIT 2;
Result:
| close (SYMBOL) |
|---|
| ACME |
| CITY |
(2 rows)
Example: A reserved word as a name needs double quotes (sample data: sample; shows an error)
SELECT symbol AS order FROM securities;
Result:
Error: line 1, column 18: expected an alias after AS but found the reserved word 'order' (write it in double quotes, "order", to use it as a name)
Example: The fix (sample data: sample)
SELECT symbol AS "order" FROM securities LIMIT 2;
Result:
| order (SYMBOL) |
|---|
| ACME |
| CITY |
(2 rows)
SELECT without FROM
Syntax:
SELECT expression [[AS] alias], ...
Without FROM, KDB computes the values once and gives one row. Use it like a calculator, or to test a function.
- Returns: one row.
- Notes:
WHEREstill works (a false condition gives 0 rows).*needs aFROMtable.
Example (sample data: sample)
SELECT 1 + 1 AS two, round(100000 * 1.08, 2) AS with_interest, upper('city') AS symbol;
Result:
| two (LONG) | with_interest (DOUBLE) | symbol (STRING) |
|---|---|---|
| 2 | 108000 | CITY |
(1 row)
Example: What is 11:00 Nepal time in UTC? (sample data: sample)
SELECT CAST('2026-09-10T11:00:00+05:45' AS TIMESTAMP) AS market_open_utc;
Result:
| market_open_utc (TIMESTAMP) |
|---|
| 2026-09-10T05:15:00.000Z |
(1 row)
Example: * needs a table (sample data: sample; shows an error)
SELECT *;
Result:
Error: line 1, column 8: * needs a FROM table
FROM
Syntax:
FROM table [[AS] alias]
FROM database.table [[AS] alias]
FROM view [[AS] alias]
FROM (SELECT ...) [AS] alias
FROM names the table the rows come from. It can also be a view or a subquery in ( ).
| Part | Meaning |
|---|---|
table | a relational or time-series table of the current database |
database.table | a table of another database on the same server (see queries across databases) |
alias | a short name for the table, used as alias.column (see table aliases) |
(SELECT ...) alias | the rows of another query; the alias is required |
- Notes: without
ORDER BY, a time-series table gives rows partition by partition, sorted by itsSORT BYcolumns (usually symbol, then time). A relational table gives rows in key order. Do not depend on this: addORDER BYwhen the order matters. - Names with capital letters or spaces can be written in double quotes:
FROM "Ochl_Day"is the same table asFROM ochl_day.
Example: A table with a short name (sample data: sample)
SELECT s.symbol, s.sector FROM securities AS s WHERE s.sector = 'Banking';
Result:
| symbol (SYMBOL) | sector (STRING) |
|---|---|
| CITY | Banking |
| COAST | Banking |
(2 rows)
Example: Rows of a subquery: trades per symbol, only the busy ones (sample data: sample)
SELECT t.symbol, t.trades
FROM (SELECT symbol, count(*) AS trades FROM trade_log GROUP BY symbol) t
WHERE t.trades > 5;
Result:
| symbol (SYMBOL) | trades (LONG) |
|---|---|
| CITY | 11 |
| COAST | 11 |
| TELCO | 6 |
(3 rows)
Example: A wrong table name (sample data: sample; shows an error)
SELECT * FROM ochl_days;
Result:
Error: line 1, column 15: table or view 'ochl_days' does not exist
DISTINCT
Syntax:
SELECT DISTINCT item, ... FROM ...
DISTINCT removes repeated rows from the result. Two rows are the same when all their output columns are equal.
- Returns: each different row once.
- NULL:
NULLvalues count as equal, so manyNULLrows become one. - Notes: to count different values, use
count(DISTINCT x)(see aggregate functions).DISTINCTworks on the output columns, afterGROUP BY. SELECT DISTINCT dayon a table with one partition per day is answered from the list of partitions, without reading rows (see answers without reading rows).
Example (sample data: sample)
SELECT DISTINCT sector FROM securities ORDER BY sector;
Result:
| sector (STRING) |
|---|
| Banking |
| Energy |
| Investment |
| Telecom |
(4 rows)
Example: Which broker bought which symbol on 2026-09-09 (sample data: sample)
SELECT DISTINCT buyer, symbol
FROM trade_log
WHERE date IN '2026-09-09'
ORDER BY buyer, symbol;
Result:
| buyer (INT) | symbol (SYMBOL) |
|---|---|
| 1 | CITY |
| 34 | CITY |
| 34 | COAST |
| 45 | CITY |
| 49 | TELCO |
| 58 | COAST |
| 58 | TELCO |
(7 rows)
Example: Cities of app users (NULL appears once) (sample data: query-app)
SELECT DISTINCT city FROM app_users ORDER BY city;
Result:
| city (STRING) |
|---|
| Dharan |
| Kathmandu |
| Lalitpur |
| Pokhara |
| NULL |
(5 rows)
Example: Count different values instead (sample data: sample)
SELECT count(DISTINCT symbol) AS symbols_traded, count(*) AS trades FROM trade_log;
Result:
| symbols_traded (LONG) | trades (LONG) |
|---|---|
| 4 | 32 |
(1 row)