# 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](where.md), [ORDER BY, LIMIT, OFFSET](order-by-limit.md), [GROUP BY and HAVING](group-by.md), [Joins](joins.md), [UNION and subqueries](union-subqueries.md), [Views](views.md) `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) ```sql 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: ```sql 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](group-by.md)) | | 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 (`CLOSE` is `close`). Text values do: `'CITY'` is not `'city'`. **Example: Trade log: the value of each trade on 2026-09-09** (sample data: sample) ```sql 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) ```sql 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) ```sql 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: ```sql 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 or `SAMPLE 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 (`EXPLAIN` shows `build 3 of 7 stored column(s) per row`), and a new column in the table does not change your result. **Example** (sample data: sample) ```sql 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) ```sql 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) ```sql SELECT *, count(*) FROM brokers; ``` Result: Error: `line 1, column 8: * cannot be combined with aggregates or SAMPLE BY` ## AS: column names Syntax: ```sql 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 close` gives one column named `close` that 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`, `UNION` and `USING` need `AS` to be an alias: `close AS left` works, `close left` does not. - An alias can be used in `ORDER BY`, but not in `WHERE`, `GROUP BY` or `HAVING` (write the expression again there). See [ORDER BY](order-by-limit.md#order-by) and [GROUP BY](group-by.md#group-by). **Example** (sample data: sample) ```sql 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) ```sql 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) ```sql 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) ```sql 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) ```sql SELECT symbol AS "order" FROM securities LIMIT 2; ``` Result: | order (SYMBOL) | |---| | ACME | | CITY | (2 rows) ## SELECT without FROM Syntax: ```sql 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:** `WHERE` still works (a false condition gives 0 rows). `*` needs a `FROM` table. **Example** (sample data: sample) ```sql 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) ```sql 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) ```sql SELECT *; ``` Result: Error: `line 1, column 8: * needs a FROM table` ## FROM Syntax: ```sql 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](views.md) or a [subquery](union-subqueries.md#from-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](cross-database-queries.md)) | | `alias` | a short name for the table, used as `alias.column` (see [table aliases](joins.md#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 its `SORT BY` columns (usually symbol, then time). A relational table gives rows in key order. Do not depend on this: add `ORDER BY` when the order matters. - Names with capital letters or spaces can be written in double quotes: `FROM "Ochl_Day"` is the same table as `FROM ochl_day`. **Example: A table with a short name** (sample data: sample) ```sql 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) ```sql 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) ```sql SELECT * FROM ochl_days; ``` Result: Error: `line 1, column 15: table or view 'ochl_days' does not exist` ## DISTINCT Syntax: ```sql 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:** `NULL` values count as equal, so many `NULL` rows become one. - **Notes:** to count different values, use `count(DISTINCT x)` (see [aggregate functions](functions-aggregate.md)). `DISTINCT` works on the output columns, after `GROUP BY`. - `SELECT DISTINCT day` on a table with one partition per day is answered from the list of partitions, without reading rows (see [answers without reading rows](explain.md#saved-stats)). **Example** (sample data: sample) ```sql 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) ```sql 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) ```sql 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) ```sql SELECT count(DISTINCT symbol) AS symbols_traded, count(*) AS trades FROM trade_log; ``` Result: | symbols_traded (LONG) | trades (LONG) | |---|---| | 4 | 32 | (1 row)