8. Q语言学习之路—表

7次阅读

共计 9961 个字符,预计需要花费 25 分钟才能阅读完成。

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. 基本的 updateupdate 的语法与 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. Findfind 操作符? 返回记录的索引
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.

正文完
 0