KDB knowledge

Queries across databases

Read a table of another database on the same server as database.table - in SELECT, JOIN, subqueries and INSERT ... SELECT.

One KDB server holds many databases. A query can read a table of another database on the same server. Write the database name, a dot and the table name: refdata.companies.

A real setup: one database refdata holds the company list. Every app database reads it from there instead of keeping its own copy.

Example: Company names from a shared reference database

Setup:

CREATE DATABASE refdata;
USE refdata;
CREATE TABLE companies (symbol STRING PRIMARY KEY, name STRING, sector STRING);
INSERT INTO companies VALUES ('CITY', 'City Bank', 'Bank'), ('HYDRO', 'River Hydro', 'Energy'), ('MART', 'Metro Mart', 'Retail');
CREATE TABLE trades (id LONG PRIMARY KEY, symbol STRING, qty LONG);
INSERT INTO trades VALUES (1, 'CITY', 100), (2, 'HYDRO', 40), (3, 'CITY', 25);
SELECT t.id, t.symbol, c.name, t.qty
FROM trades t JOIN refdata.companies c ON t.symbol = c.symbol
ORDER BY t.id;

Result:

CREATE TABLE trades (

table 'trades' created

INSERT INTO trades VALUES (1, 'CITY', 100), (2, 'HYDRO', 40), (3, 'CITY', 25)

3 row(s) inserted

SELECT t.id, t.symbol, c.name, t.qty FROM trades AS t JOIN refdata.companies AS c ON t.symbol = c.sy

id (LONG)symbol (STRING)name (STRING)qty (LONG)
1CITYCity Bank100
2HYDRORiver Hydro40
3CITYCity Bank25

(3 rows)

Name a table of another database

Syntax:

database.table
database.public.table
"database"."table"
WrittenMeans
companiesthe table in the database you are using
refdata.companiesthe table companies of the database refdata
refdata.public.companiesthe same; PostgreSQL apps write the schema public
demo.companies, when you use demoyour own database: the same as companies

What works

StatementExample
SELECTSELECT * FROM refdata.companies
JOIN, LEFT JOIN, CROSS JOIN, ASOF JOINFROM trades t JOIN refdata.companies c ON t.symbol = c.symbol
subqueries: IN, EXISTS, a value, FROM (SELECT ...)WHERE symbol IN (SELECT symbol FROM refdata.companies)
WITHWITH banks AS (SELECT * FROM refdata.companies WHERE sector = 'Bank') ...
INSERT INTO t SELECT ...copies rows from the other database into this one
CREATE TABLE t AS SELECT ...makes a copy in this database
UPDATE / DELETE with a subqueryDELETE FROM trades WHERE symbol NOT IN (SELECT symbol FROM refdata.companies)
views, procedures, triggers, EXECUTEthe other table is read each time they run

Example: Copy the banks into this database, and count trades of banks

Setup:

CREATE DATABASE refdata;
USE refdata;
CREATE TABLE companies (symbol STRING PRIMARY KEY, name STRING, sector STRING);
INSERT INTO companies VALUES ('CITY', 'City Bank', 'Bank'), ('HYDRO', 'River Hydro', 'Energy'), ('LAKE', 'Lake Bank', 'Bank');
CREATE TABLE trades (id LONG PRIMARY KEY, symbol STRING, qty LONG);
INSERT INTO trades VALUES (1, 'CITY', 100), (2, 'HYDRO', 40), (3, 'LAKE', 25);
CREATE TABLE banks (symbol STRING PRIMARY KEY, name STRING);
INSERT INTO banks SELECT symbol, name FROM refdata.companies WHERE sector = 'Bank';
SELECT count(*) AS bank_trades, sum(qty) AS bank_qty
FROM trades WHERE symbol IN (SELECT symbol FROM refdata.companies WHERE sector = 'Bank');

Result:

CREATE TABLE trades (

table 'trades' created

INSERT INTO trades VALUES (1, 'CITY', 100), (2, 'HYDRO', 40), (3, 'LAKE', 25)

3 row(s) inserted

CREATE TABLE banks (

table 'banks' created

INSERT INTO banks SELECT symbol, name FROM refdata.companies WHERE sector = 'Bank'

2 row(s) inserted

SELECT count(*) AS bank_trades, sum(qty) AS bank_qty FROM trades WHERE symbol IN (SELECT symbol FROM

bank_trades (LONG)bank_qty (LONG)
2125

(1 row)

Changes stay in your database

A statement changes only the database it runs in. INSERT, UPDATE and DELETE of otherdb.table are refused. To change the other database, run the statement there (USE refdata, or db=refdata over HTTP). From there, your database's tables can be read as yourdb.table.

Example: Writing into another database is refused (shows an error)

Setup:

CREATE DATABASE refdata;
USE refdata;
CREATE TABLE companies (symbol STRING PRIMARY KEY, name STRING, sector STRING);
INSERT INTO refdata.companies VALUES ('NEW', 'New Company', 'Bank');

Result:

Error: line 1, column 1: INSERT of table 'refdata.companies' is not allowed: a statement changes only the database it runs in ('demo'). Run it in database 'refdata' (USE refdata), where the tables of 'demo' can be read as demo.<table>

Example: A database that is not there (shows an error)

SELECT * FROM nowhere.companies;

Result:

Error: line 1, column 15: database 'nowhere' does not exist (SHOW DATABASES lists them) (in nowhere.companies)

Rights

The other table is checked with that database's rights.

To read refdata.companies a user needs
role read on refdataor GRANT SELECT ON TABLE companies made in refdata
no REVOKE SELECT ON TABLE companies in refdataa revoke there wins over the role
role admin on refdataonly for _audit_log and dropped tables

Speed

A SELECT that reads only tables of one other database, and uses nothing of yours (no table, view, saved function or type of your database), is sent to that database whole. It runs there as if you had sent it yourself: SELECT count(*) FROM market.prices is answered from the saved row counts and copies no row, and a GROUP BY or SAMPLE BY over years runs next to the data. The answer is the same.

Any other statement reads the other table into your database first. Your conditions go with the read, so it skips partitions, uses the column engine and sends only the rows that match.

ConditionGoes to the other database
WHERE of a query with only that tableyes
WHERE p.time >= ... in a join, written as alias.columnyes
a bare column name in a join (WHERE time >= ...)no: it may belong to another table
ON c.sector = 'Bank' of a JOIN or LEFT JOINyes
WHERE on the right table of a LEFT JOIN, or of an ASOF JOINno: it would change the result

Example: One day of prices from another database: only that day's partition is read

Setup:

CREATE DATABASE market;
USE market;
CREATE TABLE prices (time TIMESTAMP, symbol STRING, close DOUBLE) TIMESERIES(time) PARTITION BY DAY;
INSERT INTO prices VALUES ('2026-09-09T10:00:00Z', 'CITY', 510.0), ('2026-09-10T10:00:00Z', 'CITY', 512.5), ('2026-09-10T10:00:00Z', 'HYDRO', 88.2);
CREATE TABLE holdings (symbol STRING PRIMARY KEY, qty LONG);
INSERT INTO holdings VALUES ('CITY', 100), ('HYDRO', 50);
SELECT h.symbol, h.qty * p.close AS value
FROM holdings h JOIN market.prices p ON h.symbol = p.symbol
WHERE p.time >= '2026-09-10' AND p.time < '2026-09-11'
ORDER BY h.symbol;

Result:

CREATE TABLE holdings (

table 'holdings' created

INSERT INTO holdings VALUES ('CITY', 100), ('HYDRO', 50)

2 row(s) inserted

SELECT h.symbol, h.qty * p.close AS value FROM holdings AS h JOIN market.prices AS p ON h.symbol = p

symbol (STRING)value (DOUBLE)
CITY51250
HYDRO4410

(2 rows)

Locks