Skip to content

How to work with keyed tables

This page introduces keyed tables in q and shows how to create and work with them.

A keyed table is not a table but a dictionary (type 99h), where:

  • Its key is a table of one or more key columns.
  • Its value is a table of the non-key columns.

In table notation, write the key fields inside the square brackets. A key is a unique identifier, similar to a primary key in a traditional SQL database.

Key uniqueness

When you construct a table key, make sure its items are unique. To protect performance, KDB-X does not check that key items are unique.

There is no use case for duplicate key items, as duplicates make operation results unpredictable.

Example:

q)show market:([name:`symbol$()] address:())
name| address
----| -------

You can also add a key to an existing table using the xkey keyword or the bang operator !:

q)show t:([]sym:`MSFT`AAPL`IBM;px:98 42 126)
sym  px
--------
MSFT 98
AAPL 42
IBM  126

q)show kt:`sym xkey t           / key table on the sym column
sym | px
----| ---
MSFT| 98
AAPL| 42
IBM | 126

q)key kt
sym
----
MSFT
AAPL
IBM

q)value kt
px
---
98
42
126

q)keys kt
,`sym

q)1!t                           / key table on 1st column
sym | px
----| ---
MSFT| 98
AAPL| 42
IBM | 126

q)0!(1!t)                       / unkey table
sym  px
--------
MSFT 98
AAPL 42
IBM  126

Foreign keys

A foreign key in SQL is a column in one table whose values are members of a primary key column in another table. Foreign keys are the mechanism for establishing relations between tables.

Foreign keys in SQL provide referential integrity: an attempt to insert a foreign key value that is not in the primary key fails. This is also true in q.

You can use only the primary key of a keyed table as a foreign key, but there are other ways to link table columns:

Indexing a keyed table

There are two ways to index a keyed table.

First, index with a single row from the key, which returns a dictionary.

q)/ keyed table with single key
q)show kt:([eid:1001 1002 1003] name:`Dent`Beeblebrox`Prefect; iq:98 42 126)
eid | name       iq
----| --------------
1001| Dent       98
1002| Beeblebrox 42
1003| Prefect    126
q)flip {x cols x} key kt        / key values of the keyed table
1001
1002
1003

q)kt 1003
name| `Prefect
iq  | 126

q)/ keyed table with two keys
q)show kt:([eid:1001 1002 1003;name:`Dent`Beeblebrox`Prefect]; iq:98 42 126; shortName:`DT`BX`PT)
eid  name      | iq  shortName
---------------| -------------
1001 Dent      | 98  DT
1002 Beeblebrox| 42  BX
1003 Prefect   | 126 PT

q)flip {x cols x} key kt
1001 `Dent
1002 `Beeblebrox
1003 `Prefect

q)kt (1003;`Prefect)
iq       | 126
shortName| `PT

Second, index with a sublist from the key, which returns a list of dictionaries — that is, a table.

q)kt ([]eid:1001 1002;name:`Dent`Beeblebrox)
iq shortName
------------
98 DT
42 BX

Upserting records

upsert takes the same syntax as insert.

For an unkeyed table, upsert is equivalent to insert.

For a keyed table, it is an update if the key exists in the table, or an insert otherwise.

q)/ unkeyed table
q)show trade:([]stock:`ibm`bac`usb;price:121.3 5.76 8.19;amt:1000 500 800;time:09:03:06.000 09:03:23.000 09:04:01.000)
stock price amt  time
-----------------------------
ibm   121.3 1000 09:03:06.000
bac   5.76  500  09:03:23.000
usb   8.19  800  09:04:01.000
q)`trade insert (`ibm; 645.9; 200; 09:05:00.000)
,3
q)trade
stock price amt  time
-----------------------------
ibm   121.3 1000 09:03:06.000
bac   5.76  500  09:03:23.000
usb   8.19  800  09:04:01.000
ibm   645.9 200  09:05:00.000
q)`trade upsert (`ibm; 645.9; 200; 09:05:00.000)
`trade
q)trade
stock price amt  time
-----------------------------
ibm   121.3 1000 09:03:06.000
bac   5.76  500  09:03:23.000
usb   8.19  800  09:04:01.000
ibm   645.9 200  09:05:00.000
ibm   645.9 200  09:05:00.000

q)/ keyed table
q)show table:([stock:`intel`ibm] price:(123.2 120.4 131; 111.2 140.3 160.7))
stock| price
-----| -----------------
intel| 123.2 120.4 131
ibm  | 111.2 140.3 160.7

q)`table upsert ([stock:enlist `aapl] price:enlist 140.5 160.8 180.9)   / key absent, so insert
`table
q)table
stock| price
-----| -----------------
intel| 123.2 120.4 131
ibm  | 111.2 140.3 160.7
aapl | 140.5 160.8 180.9

q)`table upsert ([stock:enlist `ibm] price:enlist 11.2 14.3 16.7)       / key present, so update
`table
q)table
stock| price
-----| -----------------
intel| 123.2 120.4 131
ibm  | 11.2  14.3  16.7
aapl | 140.5 160.8 180.9

An alternative syntax for upsert is to use the ,: operator.

Example:

q)trade
stock price amt  time
-----------------------------
ibm   121.3 1000 09:03:06.000
bac   5.76  500  09:03:23.000
usb   8.19  800  09:04:01.000
ibm   645.9 200  09:05:00.000
ibm   645.9 200  09:05:00.000

q)trade ,: (`aapl; 122.5; 50; 09:04:59.000)
q)trade
stock price amt  time
-----------------------------
ibm   121.3 1000 09:03:06.000
bac   5.76  500  09:03:23.000
usb   8.19  800  09:04:01.000
ibm   645.9 200  09:05:00.000
ibm   645.9 200  09:05:00.000
aapl  122.5 50   09:04:59.000

Like insert, upsert can also take a table as an argument:

q)trade ,: ([] stock:`msft`msft; price:7.6 4.9; amt:300 700; time:09:06:00.000 09:07:59.000)
q)trade
stock price amt  time
-----------------------------
ibm   121.3 1000 09:03:06.000
bac   5.76  500  09:03:23.000
usb   8.19  800  09:04:01.000
ibm   645.9 200  09:05:00.000
ibm   645.9 200  09:05:00.000
aapl  122.5 50   09:04:59.000
msft  7.6   300  09:06:00.000
msft  4.9   700  09:07:59.000