0. 概述
- 表(Tables)和列表、字典一样,都是q语言中的第一类对象(First-class entity)
- Q表是*列导向的*
- Q表是从字典构建的
1.表的定义
1. 表作为列字典
q)dc:`name`iq!(`Dent`Beeblebrox`Prefect;98 42 126)
q)dc[`iq;]
98 42 126
q)dc[;2]
name| `Prefect
iq | 126
q)dc[`iq; 2]
126
我们通过flip
指令来得到一个表
q)t:flip `name`iq!(`Dent`Beeblebrox`Prefect;98 42 126)
q)t[;`iq]
98 42 126
q)t[2;]
name| `Prefect
iq | 126
q)t[2;`iq]
126
所有表的类型都是98h
。
类似于python的pandas包,可以通过t[`column]
或者t.column
来获取一个列。但请注意,点操作符在函数内不起作用。
q)t[`name`iq]
Dent Beeblebrox Prefect
98 42 126
2. 表的显示
q)dc
name| Dent Beeblebrox Prefect
iq | 98 42 126
q)t
name iq
--------------
Dent 98
Beeblebrox 42
Prefect 126
3. 表定义语法
定义语法为([] c1:L1;...;cn:Ln)
其中ci
是列名,Li
是对应的列值。其中的冒号不是必须的。
q)t:([] name:`Dent`Beeblebrox`Prefect; iq:98 42 126)
q)t~flip `name`iq!(`Dent`Beeblebrox`Prefect;98 42 126)
1b
表定义中也可以直接使用变量,这样对应的列名就是变量名了
q)c1:`Dent`Beeblebrox`Prefect
q)c2:98 42 126
q)([] c1; c2)
c1 c2
--------------
Dent 98
Beeblebrox 42
Prefect 126
atom元素会自动拓展到列长度
q)([] c1:`a`b`c; c2:42; c3:98.6)
c1 c2 c3
----------
a 42 98.6
b 42 98.6
c 42 98.6
4. 表的元数据
可以通过cols
指令来获取表的列;可以通过meta
来获取表的元数据,结果是一个键表(keyed-table)。
q)meta t
c | t f a
----| -----
name| s
iq | j
其中t
代表了列的类型,f
表示了是否存在任何外键。
指令tables
返回当前命名空间下的表名, \a
效果相同:
q)t2:([] c1:1 2 3; c2:(1 2; enlist 3; 4 5 6))
q)t:([] name:`Dent`Beeblebrox`Prefect; iq:98 42 126)
q)tables `.
`t`t2
5. 记录
表在逻辑上是一个字典记录(record)的列表,count
返回记录的个数。
q)count t
3
为了获取表中的不带列名的行,可以使用value
函数
q)value t[1]
`Beeblebrox
42
6. 转置的列字典 vs 记录的列表
逻辑上,表既可以是转置的列字典(Flipped column dictionary),也可以是记录的列表(list of records),但是物理上它被储存为列字典。事实上,Q语言会自动地识别满足表形式的列字典,在没有任何请求询问下,将其转换为表形式。
例如下两例:
q)type (`name`iq!(`Dent;98); `nome`iq!(`Beeblebrox;42))
0h
q)type (`name`iq!(`Dent;98); `name`iq!(`Beeblebrox;42))
98h
表被储存为列形式的一个坏处是,当想要删除掉表中的一行时,花销非常大。
The best way to deal with this in large tables is not to do it.
与其删除一行,不如用一个表示flag的column来表示该行有没有被删除。
2. 空表和模式
创建一般空表的方式:
q)([] name:(); iq:())
推荐的做法是给每列指定类型
q)([] name:`symbol$(); iq:`int$())
一种等价的创建带类型的表的方法:
q)([] name:0#`; iq:0#0)
q)([] name:0#`; iq:0#0)~ ([] name:`symbol$(); iq:`int$())
1b
3. 基本的select和update操作
下面以这个表的操作为例
q)t:([] name:`Dent`Beeblebrox`Prefect; iq:98 42 126)
1. select
的语法
基本语法为:
select cols from table
取全部列直接省略掉cols
即可,不用加*
。
2. 结果显示select
的结果永远是一个表。
q)select from t
name iq
--------------
Dent 98
Beeblebrox 42
Prefect 126
3. 选择列
q)select name from t
name
----------
Dent
Beeblebrox
Prefect
q)select c1:name, c2:iq from t
4. 基本的update
update
的语法与select
的语法基本相同,其使用冒号右边的值来取代该列。
q)update iq:iq%100 from t
name iq
---------------
Dent 0.98
Beeblebrox 0.42
Prefect 1.26
4. 主键和键表
在SQL中,可以声明一个或多个列作为主键,主键的值是unique的,使得通过主键的值来提取行成为可能。
1. 键表
定义:A keyed table is a dictionary mapping a table of key records to a table of value records.
一个键表并不是一个表——它是一个字典,所以类型是99h
。
2. 简单的例子
值的表(table of value):
q)v:flip `name`iq!(`Dent`Beeblebrox`Prefect;98 42 126)
键的表(table of key):
q)k:flip (enlist `eid)!enlist 1001 1002 1003
创建键表:
q)kt:k!v
q)kt
eid | name iq
----| --------------
1001| Dent 98
1002| Beeblebrox 42
1003| Prefect 126
3. 键表的定义语法
下面是通过字典形式对键表定义的常规语法:
q)kt:(flip (enlist `eid)!enlist 1001 1002 1003)! flip `name`iq!(`Dent`Beeblebrox`Prefect;98 42 126)
q)kt
eid | name iq
----| --------------
1001| Dent 98
1002| Beeblebrox 42
1003| Prefect 126
直接使用表定义语法将更为简单:
q)kt:([eid:1001 1002 1003] name:`Dent`BeebleBrox`Prefect; iq:98 42 126)
带类型的空键表的定义形式如下:
q)ktempty:([eid:`int$()] `symbol$name:(); iq:`int$())
q)ktempty
eid| name iq
---| -------
q)ktempty:([eid:0#0] name:0#`;iq:0#0)
4. 获取键表的记录
因为键表本质上是一个字典映射,所以可以通过键值对记录进行查找。注意键和值都对应的是字典。
q)kt[(enlist `eid)!enlist 1002]
name| `Beeblebrox
iq | 42
上例可以简化为:
q)kt[1002]
name| `Beeblebrox
iq | 42
我们可以获取某个列的值
q)kt[1002][`iq]
42
q)kt[1002;`iq]
42
5. 取多行的记录
q)kt[1001]
name| `Dent
iq | 98
q)kt 1001
name| `Dent
iq | 98
为了取多行的数据,通过如下的方式去取是错误的
q)kt[1001 1002]
'length
正确的方式是:
q)kt[(enlist 1001;enlist 1002)]
name iq
-------------
Dent 98
Beeblebrox 42
q)kt[flip enlist 1001 1002]
也可以通过构建一个匿名的表来取值
q)kt ([] eid:1001 1002)
name iq
-------------
Dent 98
Beeblebrox 42
回想第5.2.2节指出的,可以通过命令#
来提取一个子字典,对于键表同理:
q)([] eid:1001 1002)#kt
eid | name iq
----| -------------
1001| Dent 98
1002| Beeblebrox 42
6. 反向查找
由于键表是一个字典,所以我们通过值来反响查找键。
q)kts:([eid:1001 1002 1003] name:`Dent`Beeblebrox`Prefect)
q)kts
eid | name
----| ----------
1001| Dent
1002| Beeblebrox
1003| Prefect
反向查找:
q)kts?([] name:`Prefect`Dent)
eid
----
1003
1001
7. 键表的成分
可以通过key
和value
来提取键表成分
q)key kt
eid
----
1001
1002
1003
q)value kt
name iq
--------------
Dent 98
Beeblebrox 42
Prefect 126
函数keys
和cols
返回键表的键名和列名。
8. 表和键表
通过xkey
来设置一列为键
q)t:([] eid:1001 1002 1003; name:`Dent`Beeblebrox`Prefect; iq:98 42 126)
q)`eid xkey t
eid | name iq
----| --------------
1001| Dent 98
1002| Beeblebrox 42
1003| Prefect 126
在xkey
左运算元设置为空列表时转换为常规表
q)kt:([eid:1001 1002 1003] name:`Dent`Beeblebrox`Prefect; iq:98 42 126)
q)() xkey kt
eid name iq
-------------------
1001 Dent 98
1002 Beeblebrox 42
1003 Prefect 126
也可以使用!
的又一重载用法:左运算元为一个非负的数字,代表左侧将包含在键中的列数;右运算元为表或者键表。0
代表没有键。
q)1!t
q)0!kt
eid name iq
-------------------
1001 Dent 98
1002 Beeblebrox 42
1003 Prefect 126
q)2!0!kt
eid name | iq
---------------| ---
1001 Dent | 98
1002 Beeblebrox| 42
1003 Prefect | 126
上述返回的结果都是在表的拷贝上进行的,如果要对表进行in-place
的操作,则需要使用call-by-name
q)`eid xkey `t
`t
q)() xkey `kt
`kt
q)kt
eid name iq
-------------------
1001 Dent 98
1002 Beeblebrox 42
1003 Prefect 126
9. 复合的主键
q)ktc:([lname:`Dent`Beeblebrox`Prefect; fname:`Arthur`Zaphod`Ford]; iq:98 42 126)
q)ktc
lname fname | iq
-----------------| ---
Dent Arthur| 98
Beeblebrox Zaphod| 42
Prefect Ford | 126
复合主键的查找可以通过一个复合的键:
q)ktc[`lname`fname!`Beeblebrox`Zaphod]
iq| 42
对于一个简单的键,我们可以简化查找:
q)ktc[`Dent`Arthur]
iq| 98
空的多键值表的创建:
q)ktc:([lname:`symbol$();fname:`symbol$()] iq:`int$())
q)ktc:([lname:0#`;fname:0#`] iq:0#0)
10. 提取复合键
可以通过复合键的列表来提取对应的记录
q)ktc (`Dent`Arthur;`Prefect`Ford)
iq
---
98
126
当然也可以通过匿名表来提取
q)ktc ([] lname:`Dent`Prefect; fname:`Arthur`Ford)
当然也可以用#
来提取子字典
q)K:([] lname:`Dent`Prefect; fname:`Arthur`Ford)
q)K#ktc
lname fname | iq
--------------| ---
Dent Arthur| 98
Prefect Ford | 126
11. 提取列数据
在这一小节中我们使用如下两个例子:
q)kts:([k:101 102 103] v1:`a`b`c; v2:1.1 2.2 3.3)
q)kts
_
q)ktc:([k1:`a`b`c;k2:`x`y`z] v1:`a`b`c; v2:1.1 2.2 3.3)
q)ktc
_
提取特定列的做法:
q)kts[([] k:101 103)][`v1]
`a`c
q)ktc[([] k1:`a`c;k2:`x`z)][`v1`v2]
也可以使用index at depth
的做法
q)kts[([] k:101 103); `v1]
_
q)ktc[([] k1:`a`c;k2:`x`z); `v1`v2]
_
5. 外键和虚拟列
外键是指表中的一列,该列的数据都在另一个表中的主键中。
1. 外键的定义
定义:A foreign key is one or more table columns whose values are defined as an enumeration over the key column(s) of a keyed table.
2, 外键的例子
以下表为例
q)kt:([eid:1001 1002 1003] name:`Dent`Beeblebrox`Prefect; iq:98 42 126)
以eid列为外键的表定义如下,这里用到了枚举,强制保证了该列出现的值都在另一个表的键值中
q)tdetails:([] eid:`kt$1003 1001 1002 1001 1002 1001; sc:126 36 92 39 98 42)
在meta
命令中,可以看到外键出现在f
列中:
q)meta tdetails
c | t f a
---| ------
eid| j kt
sc | j
内置函数fkeys
返回一个表示外键的字典
q)fkeys tdetails
eid| kt
3. 清除外键
当想清楚一个外键时,对枚举的列使用value
函数
q)meta update value eid from tdetails
c | t f a
---| -----
eid| j
sc | j
4. 外键和关系
设tf
是一个带有键表kt
外键f
的表,为了获取kt
中的某一列c
,可以直接在select
语句中直接使用点操作符f.c
即可。
q)select eid.name, sc from tdetails
name sc
--------------
Prefect 126
Dent 36
Beeblebrox 92
Dent 39
Beeblebrox 98
Dent 42
6. 使用表和键表
在这一节中,我们使用如下例子
q)t:([] name:`Dent`Beeblebrox`Prefect; iq:98 42 126)
q)kt:([eid:1001 1002 1003] name:`Dent`Beeblebrox`Prefect; iq:98 42 126)
1. Append记录
使用,:
,带列名时可以不同考虑顺序
q)t,:`name`iq!(`W; 26)
q)t,:`iq`name!(200; `Albert)
q)t
name iq
--------------
Dent 98
Beeblebrox 42
Prefect 126
Albert 200
不带列名时必须考虑顺序
q)t,:(`H; 142)
_
q)t,:(97;`J)
'type
q)t
_
2. First和Last记录
使用函数first
和last
q)first t
name| `Dent
iq | 98
q)last t
name| `Albert
iq | 200
使用#
获取前后几行记录
q)2#t
q)-3#kt
3. Find
find操作符?
返回记录的索引
q)t?`name`iq!(`Dent;98)
0
上式可简化为
q)t?(`Dent;98)
0
也可以查多列
q)t?((`Dent;98);(`Prefect;126))
0 2
由于键表是一个字典,所以使用?
相当于执行了反向查找,返回对应的键值
q)kt?`name`iq!(`Dent;98)
eid| 1001
q)kt?(`Dent;98)
eid| 1001
单个列的查找必须生成列值,或者使用匿名表的结构
q)t1:([] eid:1001 1002 1003)
q)t1?enlist each 1001 1002
0 1
q)t1?([] eid:1001 1002)
0 1
4. Union with,
使用join操作符,
追加记录到一个表中,但是不会执行类型检查。
q)t,`name`iq!(`Slaartibartfast; `123)
name iq
--------------------
Dent 98
Beeblebrox 42
Prefect 126
Slaartibartfast `123
只有表有完全一样的meta
结果时,两个表才能合并成一个表。
q)t,([] name:1#`W; iq:1#26)
q)t,t
当键表使用,
合并两个meta
结果相同的键表时,右边的键表将upsert(update and insert)左边的键表,不同的键将append,相同的键将update。
q)kt,([eid:1003 1004] name:`Prefect`W; iq:150 26)
eid | name iq
----| --------------
1001| Dent 98
1002| Beeblebrox 42
1003| Prefect 150
1004| W 26
5. Coalesce ^
Coalesce^
被用来合并两个具有相同列的键表。^
的效果与,
的相同,除了这种情况:两个键表相同键合并时,右边的键表若为null
,则左边的保留下来。
q)([k:`a`b`c] v:10 0N 30)^([k:`a`b`c] v:100 200 0N)
k| v
-| ---
a| 100
b| 200
c| 30
q)([k:`a`b`c`x] v:10 0N 30 40)^([k:`a`b`c`y]; v:100 200 0N 0N)
k| v
-| ---
a| 100
b| 200
c| 30
x| 40
y|
^
的执行效率没有,
的高,因为右运算元需要检查是否为null
。
6. 列 Join
两个具有相同数量记录的表可以使用join-each(,'
)来合并列。
q)([] c1:`a`b`c),'([] c2:100 200 300)
c1 c2
------
a 100
b 200
c 300
在键表上的列join:
q)([k:1 2 3] v1:10 20 30),'([k:3 4 5] v2:1000 2000 3000)
k| v1 v2
-| -------
1| 10
2| 20
3| 30 1000
4| 2000
5| 3000
7. 复杂列数据
1. 简单的例子
在如下表中,lh列中储存了一个嵌套的列表
q)tp:([] d:2015.01.01 2015.01.02; lh:(67.9 82.10; 72.8 88.4))
q)tp 0
d | 2015.01.01
lh| 67.9 82.1
q)tp `lh
67.9 82.1
72.8 88.4
2. 复合列数据的操作
复合列数据的定义:嵌套列中的元素都是简单列表。
q)tm:([] wk:2015.01.01 2015.01.08; rv:(38.92 67.34; 16.99 5.14 128.23 31.69))
q)tm
wk rv
----------------------------------
2015.01.01 38.92 67.34
2015.01.08 16.99 5.14 128.23 31.69
Nested columns mean adverbs. Lots of adverbs.
q)select wk, srt:desc each rv, avgr:avg each rv, hi:max each rv from tm
wk srt avgr hi
-------------------------------------------------
2015.01.01 67.34 38.92 53.13 67.34
2015.01.08 128.23 31.69 16.99 5.14 45.5125 128.23
q)select wk, drp:neg 1_'deltas each desc each rv from tm
wk drp
---------------------------
2015.01.01 ,28.42
2015.01.08 96.54 14.7 11.85
3. 复合外键
复合主键的键表:
q)ktc:([lname: `Dent`Beeblebrox`Prefect; fname:`Arthur`Zaphod`Ford]; iq:98 42 126)
有ktc
外键的表:
q)tdetails:([] name:`ktc$(`Beeblebrox`Zaphod;`Prefect`Ford;`Beeblebrox`Zaphod); sc:36 126 42)
ktc
的列就可以作为tdetails
的虚拟列了
q)select name.lname, name.iq, sc from tdetails
lname iq sc
------------------
Beeblebrox 42 36
Prefect 126 126
Beeblebrox 42 42
8. 属性
Attributes are metadata that you attach to lists of special forms. They are also used on a dictionary domain or a table column to speed retrieval for some operations.
属性是描述性的,当你指定表中某一列具有某种属性时,q语言会去检查确认,但并不会帮你完成这个操作。
Kx says not to expect significant benefit from an attribute for fewer than a million items.
1. Sorted `s#
当某一列被指定为sorted的时候,线性搜索就会被替换为二分查找,查找速度更快。
当属性被成功应用于列表时,它就变成了列表的一部分,并且q会检查这个列表是否满足这一属性。
q)`s#1 2 4 8
`s#1 2 4 8
q)`s#2 1 3 4
's-fail
排序函数asc
会自动在其结果上应用sorted属性,但是til
不会
q)asc 2 1 8 4
`s#1 2 4 8
q)til 5
0 1 2 3 4
q)L:`s#1 2 3 4 5
q)L,:6
q)L
`s#1 2 3 4 5 6
q)L,:0
q)L
1 2 3 4 5 6 0
对字典应用sorted属性,会在应用到其键值上,其查找算法就会被替代为二分查找。
q)d:`s#10 20 30 40 50!`a`b`c`d`e
q)key d
`s#10 20 30 40 50
q)d 10
`a
q)d 12
`a
q)d 15
`a
q)d 20
`b
2. Unique `u#
q)`u#2 1 4 8
`u#2 1 4 8
q)`u#2 1 4 8 2
'u-fail
Unique属性的amend操作:
q)L:`u#2 1 4 8
q)L,:3
q)L
`u#2 1 4 8 3
q)L,:2
q)L
2 1 4 8 3 2
unique属性可以作用于字典的域,表的一列或者键表的键。但是不能作用于一个字典,表或者键表。
3. Parted `p#
Parted属性 `p#
表明对所有出现的值,相同的值都是互相邻近的。
q)`p#2 2 2 1 1 4 4 4 4 3 3
`p#2 2 2 1 1 4 4 4 4 3 3
q)`p#2 2 2 1 1 4 4 4 4 3 3 2
'u-fail
[0] `p#2 2 2 1 1 4 4 4 4 3 3 2
parted属性在任何list的操作上都不会被保留下来,即使折耳根操作保留了这个属性。
q)L:`p#1 1 2 3 3
q)L
`p#1 1 2 3 3
q)L,:3
q)L
1 1 2 3 3 3
4. Grouped `g#
可以作用在任何list上。
It causes q to create and maintain an index – essentially a hash table.
q)`g#1 2 3 2 3 4 3 4 5 2 3 4 5 4 3 5 6
q)L:`g#100?100
q)L,:1 1 1 1
5. 去除属性 `#
通过指令 `#
可以去除列表上的任何属性:
q)L:`s#til 10
q)L
`s#0 1 2 3 4 5 6 7 8 9
q)`#L
0 1 2 3 4 5 6 7 8 9
Section End.