Q语言——表的高级查询语言(一)

前言

Q的表的基本操作与传统数据库的表的操作非常的相似,同时Q的表的操作还有一些传统数据库不具有的高级操作。但是可能因为KDB数据库的特性,在参数和语句上有些区别。

第一个重要区别是Q表的行和列都是有序的。这在处理那些按照时间顺序来记录的数据时非常有用的。将数据追加到表中确保是按照顺序的并保持不变,这对后续的select操作非常重要,就无需再排序。

第二个区别是Q表在物理上存储为列的一个列表(list)集合。这意味着对列数据的操作是向量操作。此外,对于简单列的列表,应用于列的原子,聚合和统一函数特别简单和快速,因为它们会减少直接存储器寻址。

第三个区别是q-sql提供了upsert语义。在字典上的更新是使用“,:”操作,意味着当应用键值对,并且键存在时,值会更新;否则插入该键值对。在表和键表的上下文中,upsert语句有着不同的操作,具体看下面的介绍。

在本篇中,我们将介绍q-sql的重要功能,从每个的简单示例开始,我们可能会使用到官方的一个案例脚本sp.q(具体可以去官方下载或者私信我)。

一、 表的数据插入

我们有多种方法可以给表中插入数据,有前面介绍的“,:”,insert,upsert。

1. “,:”插入

q)t:([] name:`symbol$(); iq:`int$()) /创建一个空表t
q)t
name iq
-------
q)t,:`name`iq!(`zhangsan; 42) /利用字典的形式插入数据
q)t
name iq
-----------
zhangsan 42
q)t,:`name`iq!(`lisi; 98.0) /当插入的数据类型不对时会报错,98.0不是int类型
'type
q)t,:(`wangwu; 126) /快速简单的给表t中插入数据
q)t
name iq 
------------
zhangsan 42 
wangwu 126
q)t,:(`wangwu; 126) /当添加相同数据时会重复添加
q)t /这时我们看到会有两条相同的wangwu 126的数据
name iq 
------------
zhangsan 42 
wangwu 126
wangwu 126
q)kt:([eid:`long$()] name:`symbol$(); iq:`long$()) /创建一个含有主键的空表kt
q)kt,:(1002; `zhangsan; 98) /给含有主键的表中插入数据
q)kt
eid | name iq
----| -----------
1002| zhangsan 98
q)kt,:(1002; `lisi; 101) /当插入的数据中主键已经有该记录时(1002)会更新,而不是重复插入
q)kt
eid | name iq 
----| --------
1002| lisi 101
q)kt,:(1001; `zhangsan; 101) /当插入的数据中随后value值相同,但是主键中不存在该记录时会添加该记录
q)kt
eid | name iq 
----| ------------
1002| lisi 101
1001| zhangsan 101

2. insert插入

Q语言中也有insert插入操作,其语法模板如下:

`Table_name insert Vaule

左边是要插入的表名(一同要时`table_name的形式,前面介绍过这种方式叫pass by name),右边是要插入的数据。其返回结果是一个插入的当前行号。

q)t:([] name:`zhangsan`lisi`wangwu; iq:42 98 126) /创建一个表t
q)t
name iq 
------------
zhangsan 42 
lisi 98 
wangwu 126
q)`t insert (`name`iq)!(`wanger; 134) /使用字典的形式插入数据
,3 /返回的结果是当前插入的行号
q)t
name iq 
------------
zhangsan 42 
lisi 98 
wangwu 126
wanger 134
q)`t insert (`sunwukong; 150) /使用普通列表的形式插入,(`sunwukong; 150)为一个普通列表
,5
q)`t insert (`sunwukong; 150) /相同的记录会重复插入
,6
q)t
name iq 
-------------
zhangsan 42 
lisi 98 
wangwu 126
wanger  134
sunwukong 150
sunwukong 150
q)t:3#t /这里3#t表示只取表t的前3行数据
q)t
name iq 
------------
zhangsan 42 
lisi 98 
wangwu 126
q)`t insert (`name`iq!(`Slar; 134); (`name`iq!(`Marvin; 200))) /同时插入多个数据的第一种方法
3 4
q)t
name iq 
------------
zhangsan 42 
lisi 98 
wangwu 126
Slar 134
Marvin 200
q)t:3#t
q)`t insert ([] name:`Slar`Marvin; iq:134 200) /同时插入多个数据的第二种方法,该方法就是将一个表插入到另外一个表中
3 4
q)t
name iq 
------------
zhangsan 42 
lisi 98 
wangwu 126
Slar 134
Marvin 200
q)insert[`t; (`Slar; 134)] /使用前缀的形式来插入数据
,5
q)t
name iq 
------------
zhangsan 42 
lisi 98 
wangwu 126
Slar  134
Marvin 200
Slar 134
q)`t insert ((`Prefect; 126); (`Marvin; 200)) /这里同时插入多个数据不能以这种形式,因为这是一个嵌套列表,无法跟表的列对应上,因此会报错
'type
q)`t insert (`Prefect`Marvin; 126 200) /多个数据的通过普通列表的插入方式
6 7
q)t
name iq 
------------
zhangsan 42 
lisi 98 
wangwu 126
Slar 134
Marvin 200
Slar 134
Prefect 126
Marvin 200
q)t:([] name:(); iq:()) /创建一个未指定数据类型的空表t
q)t
name iq
-------
q)`t insert (`Dent;98) /为制定数据类型的空表t的数据类型由第一次插入的数据类型来决定后续只能插入那种数据类型
,0
q)t
name iq
-------
Dent 98
q)meta t /这时我们可以看书name字段类型是symbol,iq的数据类型是long类型
c | t f a
----| -----
name| s 
iq | j 
q)`t insert (`Beeblebrox; 42.0) /此时我们给表t中插入一条含有float类型的数据则添加失败
'type
q)tnew /我们可以看是否有一个表叫tnew的,返回结果错误,表示内存中目前不包含这个表
'tnew
q)`tnew insert enlist `c1`c2!(`a; 10) /此时我们给未曾创建的tnew表中插入数据,Q会自动来创建tnew表并添加对应数据
,0
q)tnew
c1 c2
-----
a 10
q)`c1`c2!(`a; 10) /这是一个字典
c1| `a
c2| 10
q)enlist `c1`c2!(`a; 10) /因此我们需要用enlist转换成表才能成功`tnew insert enlist `c1`c2!(`a; 10)这个操作
c1 c2
-----
a 10
q)kt:([eid:1001 1002 1003] name:`Dent`Beeblebrox`Prefect; iq:98 42 126) /创建一个含有主键的kt表,主键是eid
q)kt
eid | name iq 
----| --------------
1001| Dent 98 
1002| Beeblebrox 42 
1003| Prefect 126
q)kt_foreign:([] eid:`kt$1003 1002 1001 1002 1001; sc:126 36 92 39 98) /创建一个外键表kt_foreign,对应的外键是kt表的eid主键
q)kt_foreign
eid sc 
--------
1003 126
1002 36 
1001 92 
1002 39 
1001 98 
q)`kt_foreign insert (1002;42) /给含有外键的表中插入数据
,5
q)kt_foreign
eid sc 
--------
1003 126
1002 36 
1001 92 
1002 39 
1001 98 
1002 42 
q)`kt_foreign insert (1004;42) /同样当kt表中的eid主键不包含1004这个记录时,给kt_foreign表中添加1004这条数据也不会成功
'cast
q)kt:([eid:1001 1002] name:`Dent`Beeblebrox; iq:98 42) /创建一个含有主键的kt表,主键是eid
q)kt
eid | name iq
----| -------------
1001| Dent 98
1002| Beeblebrox 42
q)`kt insert (1005; `Marvin; 200) /给kt表中添加数据
,2
q)`kt insert (1004;`Slartibartfast;158)
,3
q)`kt insert (1004;`Slar;158) /同样插入的数据eid字段中包含该记录(1004)时,插入会失败
'insert
q)kt,:(1004;`sla;158) /但是该方式就会成功
q)kt
eid | name iq 
----| --------------
1001| Dent 98 
1002| Beeblebrox 42 
1005| Marvin 200
1004| sla 158

3. upsert插入

upsert与insert相似,只是upsert在含有主键的表上比insert插入方式更加灵活。

q)t:([] name:`Dent`Beeblebrox`Prefect; iq:42 98 126)
q)t
name iq 
--------------
Dent 42 
Beeblebrox 98 
Prefect 126
q)`t upsert (`name`iq)!(`sla;134) /upsert的字典插入形式
`t
q)t
name iq 
--------------
Dent 42 
Beeblebrox 98 
Prefect 126
sla 134
q)`t upsert (`Marvin; 150) /upsert的列表插入形式
`t
q)t
name iq 
--------------
Dent 42 
Beeblebrox 98 
Prefect 126
sla 134
Marvin 150
q)`t upsert ([] name:`Slartibartfast`Marvin; iq:134 200) /upsert的表的插入形式
`t
q)t
name iq 
------------------
Dent  42 
Beeblebrox 98 
Prefect  126
sla 134
Marvin 150
Slartibartfast 134
Marvin 200
q)upsert[`t; (`Slartibartfast; 134)] /upsert的前缀插入形式
`t
q)([] c1:`a`b; c2:10 20) upsert (`c; 30) /upsert的独特插入方式
c1 c2
-----
a 10
b 20
c 30
q)f:{t:([] c1:`a`b; c2:10 20); t upsert x} /我们可以把upsert的插入方式写成函数脚本的形式,这样后续插入的时候就更加方便
q)f(`c;30) /以函数的形式插入数据
c1 c2
-----
a 10
b 20
c 30
q)kt
eid | name iq 
----| --------------
1001| Dent 98 
1002| Beeblebrox 42 
1005| Marvin 200
1004| sla 158
q)`kt upsert (1001; `Beeblebrox; 42) /upsert给含有主键的表中插入数据,其中已经包含1001这条数据,也能够成功
`kt
q)kt
eid | name iq 
----| --------------
1001| Beeblebrox 42 
1002| Beeblebrox 42 
1005| Marvin 200
1004| sla 158
q)`kt upsert (1001; `Beeblebrox; 43) /当主键中存在1001这条数据时,再次插入这条数据就是更新了,而不是insert的不能插入
`kt
q)kt
eid | name iq 
----| --------------
1001| Beeblebrox 43 
1002| Beeblebrox 42 
1005| Marvin 200
1004| sla 158

二、 表的数据查询(select语句)

在本节中,我们将分享select查询语句,select查询语句包含必要参数和可选参数,最终解释器转换为函数形式,并应用于表查询后的返回结果也是表。虽然select类似于类似SQL语句的语法和行为,但潜在的机制却截然不同。

我们详细研究了每个组成部分select。通过实际案例来分析。

该select查询语句具有以下形式:

select< ps > < by pb > from texp < where pw >

selectfrom关键字是必需的; texp为表的名称。其余元素pspbpw是可选的。它们分别称为selectbywhere子句。这里还要注意分隔子句的逗号的使用,具体见案例。这里from表达式texp(主要是表的名称);where子句pw(主要是限制条件等);by子句pb(主要是by的聚合);select子句ps(主要列的选择)。

Select语句的执行顺序是:

select查询顺序

1. select子句

q)t:([] c1:`a`b`c; c2:10 20 30; c3:1.1 2.2 3.3)
q)t /查询表t的所有内容
c1 c2 c3 
---------
a 10 1.1
b 20 2.2
c 30 3.3
q)select from t /查询表t的所有内容,而SQL中是select * from t
c1 c2 c3 
---------
a 10 1.1
b 20 2.2
c 30 3.3
q)t~select from t /这两种查询方式是一样的
1b
q)select c1, c3 from t /查询表t中的c1列和c3列的所有内容
c1 c3 
------
a 1.1
b 2.2
c 3.3
q)select avg c2 from t /我们也可以同时使用内置函数来操作查询结果,这里返回c2字段的平均值
c2
--
20
q)select c1, res:2*c2 from t /查询表t中c1列和c2列,并给c2列所有值乘以2然后将该列重命名为res。这里并不会改变表t的内容
c1 res
------
a 20 
b 40 
c 60 

当我们查询语句中的列名重复时,系统会自动重命名,重命名的方式就是名称后面加一个1(如c1和c11);当有表达式时(c2+c3)系统会自动以第一个列名作为返回的列名,而不是以表达式命名;若表达式第一个不是列名,则以x作为返回的列名(如2*c2),同理多个这样的表达式则为x,x1,x2等形式。下面的两个查询语句可以仔细领会一下。

q)select c1, c1, 2*c2, c2+c3, string c3 from t 
c1 c11 x c2 c3 
--------------------
a a 20 11.1 "1.1"
b b 40 22.2 "2.2"
c c 60 33.3 "3.3"
q)select c1, c1, c1, 2*c2, 2*c2, c2+c3, c3+c2, 2%c3, c2%c3, string c3 from t
c1 c11 c12 x x1 c2 c3 x2 c21 c31 
---------------------------------------------------
a a a 20 20 11.1 11.1 1.818182 9.090909 "1.1"
b b b 40 40 22.2 22.2 0.9090909 9.090909 "2.2"
c c c 60 60 33.3 33.3 0.6060606 9.090909 "3.3"

Q的表中含有一个虚拟列i,虚拟列i表示表中每条记录的行号。

q)select i, c1 from t /这里虚拟列i不能直接返回一个i作为返回的列名,用x来自动重命名。
x c1
---- 
0 a 
1 b 
2 c 
q)select ix:i, c1 from t /我们可以给虚拟列i重命名
ix c1
-----
0 a 
1 b 
2 c 
q)t1:([] c1:`a`b`a; c2:10 20 10)
q)select distinct from t1 /我们可以使用distinct关键字来进行查询结果去掉重复的内容
c1 c2
-----
a 10
b 20
q)show tnest:([] c1:`a`b`c; c2:(10 20 30; enlist 40; 50 60))
c1 c2 
-----------
a 10 20 30
b ,40 
c 50 60 
q)select avg c2 from tnest /这里由于tnest表的c2字段值不是简单列表,因此无法直接使用内置原子函数avg(求平均值),需要用each修饰符
'length
q)select avg each c2 from tnest /使用了each修饰符之后就能够查询并计算成功
c2
--
20
40
55
q)update c3:(1.1 2.2 3.3; enlist 4.4; 5.5 6.6) from `tnest /这里使用update给表tnest增加一列,后面会针对性的讲解update操作
`tnest
q)tnest
c1 c2 c3 
-----------------------
a 10 20 30 1.1 2.2 3.3
b ,40 ,4.4 
c 50 60 5.5 6.6 
q)select wtavg:c2 wavg' c3 from tnest /这里wtavg:c2 wavg' c3表示求加权平均,c2列的值为权重
wtavg 
--------
2.566667
4.4  
6.1 

2. where的过滤条件

与SQL语言相似,条件查询也是使用where关键字。where后面一般跟的都是条件设置
q)t:([] c1:`a`b`c; c2:10 20 30; c3:1.1 2.2 3.3)
q)t
c1 c2 c3 
---------
a 10 1.1
b 20 2.2
c 30 3.3
q)select from t where c2>15 /限制条件为c2字段中的值大于15的记录
c1 c2 c3 
---------
b 20 2.2
c 30 3.3
q)t where t[`c2]>15 /一种较为简便的where查询方式
c1 c2 c3 
---------
b 20 2.2
c 30 3.3
q)select from t where 011b /限制条件也可以是布尔值,从第0行开始,1代表选中,0代表不选中该行,如010b表示选中第二行
c1 c2 c3 
---------
b 20 2.2
c 30 3.3
q)tbig:100#t /将100#t表示将表t按照顺序复制100行,则是100%3=33余1,也就是33个表t加第一行
q)tbig
c1 c2 c3 
---------
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
.. /省略号表示未显示完全
q)select from tbig where i within 50 99 /也可以利用虚拟列i来作为限制条件,within限制条件是取50~99之间的行
c1 c2 c3 
---------
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
b 20 2.2
c 30 3.3
a 10 1.1
..
q)select from tbig where i within 50 52
c1 c2 c3 
---------
c 30 3.3
a 10 1.1
b 20 2.2
q)s:50
q)e:52
q)select from tbig where i within (s;e) /这里within也可以使用参数的形式
c1 c2 c3 
---------
c 30 3.3
a 10 1.1
b 20 2.2
q)select from t where c2>15,c3<3.0 /同时多个限制条件,这里的“,”号表示和的意思
c1 c2 c3 
---------
b 20 2.2
q)select from t where (c2>15)&c3<3.0 /使用&符号时这里的括号就是必须的了
c1 c2 c3 
---------
b 20 2.2
q)t:([] c1:00:00:00.000+til 1000000; c2:1000000?`a`b; c3:1000000?100.)
q)t
c1 c2 c3 
------------------------
00:00:00.000 a 39.27524
00:00:00.001 a 51.70911
00:00:00.002 a 51.59796
00:00:00.003 a 40.66642
00:00:00.004 b 17.80839
00:00:00.005 b 30.17723
00:00:00.006 a 78.5033 
00:00:00.007 a 53.47096
00:00:00.008 a 71.11716
00:00:00.009 a 41.1597 
00:00:00.010 a 49.31835
00:00:00.011 a 57.85203
00:00:00.012 b 8.388858
00:00:00.013 a 19.59907
00:00:00.014 a 37.5638 
00:00:00.015 b  61.37452
00:00:00.016 a 52.94808
00:00:00.017 b 69.16099
00:00:00.018 b 22.96615
00:00:00.019 a 69.19531
..
q)\t select from t where c1 within 00:00:01.000 00:00:01.999, c2=`a /多个限制条件查询时,不同的顺序查询的时间是不同个的,这里\t为显示执行这条语句的时间,结果是1秒
1
q)\t select from t where c2=`a, c1 within 00:00:01.000 00:00:01.999 /同样的限制条件,但是顺序不同,这里的时间就是12秒了
12

对于表的中某一个字段不是简单列表时,使用where限制条件与前面一样,也是需要用到each修饰符

q)t:([] f:1.1 2.2 3.3; s:("abc";enlist "d";"ef"))
q)t
f s 
---------
1.1 "abc"
2.2 ,"d" 
3.3 "ef" 
q)select from t where s="ef"  /这里只是判断字段s是否等于“ef”
'length
q)select from t where s~"ef" /这里只是判断字段s是否能与“ef”匹配
f s
---
q)select from t where s~\:"ef" /这里\:是前面介绍的each-left,让”ef”来匹配s字段中的每一个值
f s 
--------
3.3 "ef"
q)select from t where s like "ef" /当然我们也可以使用like关键字,这样更加方便
f s  
--------
3.3 "ef"

这里为了方便,我就直接引入了官方给定的案例表格,可以通过下面的方式导入脚本。导入后内存中就会创建三个表,分别是表s,p和sp。

q)\l /Users/souosamusan/q/sp.q /脚本的导入方式,通过该脚本创建三个表,分别是表s,p和sp
q)s /表s
s | name status city 
--| -------------------
s1| smith 20 london
s2| jones 10 paris 
s3| blake 30 paris 
s4| clark 20 london
s5| adams 30 athens
q)p /表p
p | name color weight city 
--| -------------------------
p1| nut red 12 london
p2| bolt green 17 paris 
p3| screw blue 17 rome 
p4| screw red 14 london
p5| cam blue 12 paris 
p6| cog red 19 london
q)sp /表sp
s p qty
---------
s1 p1 300
s1 p2 200
s1 p3 400
s1 p4 200
s4 p5 100
s1 p6 100
s2 p1 300
s2 p2 400
s3 p2 200
s4 p2 200
s4 p4 300
s1 p5 400
q)meta s /查看表s的结构信息
c | t f a
------| -----
s | s 
name | s 
status| j 
city | s 
q)meta p /查看表p的结构信息
c | t f a
------| -----
p | s 
name | s 
color | s 
weight| j 
city | s 
q)meta sp /查看表sp的结构信息,sp有两个外键,分别来自表s和表p
c | t f a
---| -----
s | s s 
p | s p 
qty| j 
q)select[2] from s where city<>`athens /这里的限制条件时不等于,同时select[2]表示只取查询结果的前两条数据
s | name status city 
--| -------------------
s1| smith 20 london
s2| jones 10 paris 
q)select[-1] from s where city<>`athens /这里的限制条件时不等于,同时select[-1]表示只取查询结果的倒数第一条数据
s | name status city 
--| -------------------
s4| clark 20 london
q)select from s where city<>`athens /这里的限制条件是不等于
s | name status city 
--| -------------------
s1| smith 20 london
s2| jones 10 paris 
s3| blake 30  paris 
s4| clark 20 london
q)2#select from s where city<>`athens 这里的限制条件时不等于,同时2#表示只取查询结果的前两条数据
s | name status city 
--| -------------------
s1| smith 20 london
s2| jones 10 paris 
q)-1#select from s where city<>`athens /这里的限制条件时不等于,同时-1#表示只取查询结果的倒数第一条数据
s | name status city 
--| -------------------
s4| clark 20 london
q)select[1 2] from s where city<>`athens /这里的限制条件时不等于,同时select[1 2]表示只取查询结果的第2行和第3行数据
s | name status city 
--| ------------------
s2| jones 10 paris
s3| blake 30 paris
q)select[>name] from s where city<>`athens /这里的限制条件时不等于,同时select[>name]表示将查询结果的按照name字段中的降序(字母z-a)排列
s | name status city 
--| -------------------
s1| smith 20 london
s2| jones 10 paris 
s4| clark 20 london
s3| blake 30 paris 
q)select[`athens /这里的限制条件时不等于,同时select[name] from s where city<>`athens /这里的限制条件时不等于,同时select[2;>name]表示将查询结果的按照name字段中的降序(字母z-a)排列并且只取前两条数据
s | name status city 
--| -------------------
s1| smith 20 london
s2| jones 10 paris 
q)p
p | name color weight city 
--| -------------------------
p1| nut red 12 london
p2| bolt green 17 paris 
p3| screw blue 17 rome 
p4| screw red 14 london
p5| cam blue 12 paris 
p6| cog red 19 london

在SQL中你会使用HAVING,但Q语言中没有HAVING子句。而是fby在where短语中使用。由于它返回每个组中聚合的值,因此只需将查询目标列与fby结果进行比较,最后就可以返回想要的结果。

fagg ; exprcol)fby c

括号中的操作数包含两项,包括一个聚合函数fagg 和一个列的表达式。c是要分组的列。

q)select from p where weight=(max;weight) fby city /这里首先时fby将city字段进行合并,相当于把所有city字段中相同的值作为一组,然后weight=(max;weight)再去进行匹配查找相关记录
p | name color weight city 
--| -------------------------
p2| bolt green 17 paris 
p3| screw blue 17 rome 
p6| cog red 19 london
q)select from p where weight=(max;weight) fby city, color=`blue /这里首先时fby将city字段进行合并,并返回color字段中值为blue的疾苦,然后weight=(max;weight)再去进行匹配查找相关记录
p | name color weight city
--| -----------------------
p3| screw blue 17 rome
q)select max weight by city from p /使用内置函数查询max weight表示选择weight中最大的值返回,by子句是分组查询,by city就是先将city字段中的相同值都放在一起,然后max weigh去查找每组中最大的weight
city | weight
------| ------
london| 19 
paris | 17 
rome | 17 
q)select name, color, max weight by city from p /使用内置函数查询,并结合by子句分组
city | name color weight
------| ----------------------------------
london| `nut`screw`cog `red`red`red 19 
paris | `bolt`cam `green`blue 17 
rome | ,`screw ,`blue 17 
q)select first name, first color, max weight by city from p /使用内置函数查询,by city分组后再去查询first name, first color, max weight等结果
city | name color weight
------| ------------------
london| nut red 19 
paris | bolt green 17 
rome | screw blue 17 

3. 分组与聚合

在SQL中,分组和聚合是一起执行,在Q语言中它们是独立的。在本节中,我们使用sp.q脚本中定义的s、p和sp表。

q)s
s | name status city 
--| -------------------
s1| smith 20 london
s2| jones 10 paris 
s3| blake 30 paris 
s4| clark 20 london
s5| adams 30 athens
q)p 
p | name color weight city 
--| -------------------------
p1| nut red 12 london
p2| bolt green 17 paris 
p3| screw blue 17 rome 
p4| screw red 14 london
p5| cam  blue 12 paris 
p6| cog red 19 london
q)sp
s p qty
---------
s1 p1 300
s1 p2 200
s1 p3 400
s1 p4 200
s4 p5 100
s1 p6 100
s2 p1 300
s2 p2 400
s3 p2 200
s4 p2 200
s4 p4 300
s1 p5 400

1) 无分组的聚合

Q语言中提供了很多的聚合函数,如sum、avg、max和min。我们可以直接将这些聚合函数用于整个表,不进行分组,这里就可以得到整个表的一些想要查的信息。

q)select total:sum qty, mean:avg qty from sp /将聚合函数用于整个表中
total mean 
--------------
3100 258.3333

2) 无聚合的分组

我们可以用子句来将一个表中某些字段中具有相同的数据的值进行分组,非常类似于SQL中的group by子句。含有by子句的查询结果是一个由主键的表,其中主键是所分组的列。

q)t:([] c1:`a`b`a`b`c; c2:10 20 30 40 50)
q)t
c1 c2
-----
a 10
b 20
a 30
b 40
c 50
q)select c2 by c1 from t /将c2的值由c1进行分组,结果为c1是主键的一张表
c1| c2 
--| -----
a | 10 30
b | 20 40
c | ,50 
q)ungroup select c2 by c1 from t /我们也可以使用ungroup关键字将分组的结果进行解组
c1 c2
-----
a 10
a 30
b 20
b 40
c 50
q)sp
s p qty
---------
s1 p1 300
s1 p2 200
s1 p3 400
s1 p4 200
s4 p5 100
s1 p6 100
s2 p1 300
s2 p2 400
s3 p2 200
s4 p2 200
s4 p4 300
s1 p5 400
q)`p xgroup sp /还有最快捷的方法,我们直接使用xgroup进行分组,而不需要使用select关键字
p | s qty 
--| -------------------------------
p1| `s$`s1`s2 300 300 
p2| `s$`s1`s2`s3`s4 200 400 200 200
p3| `s$,`s1 ,400 
p4| `s$`s1`s4 200 300 
p5| `s$`s4`s1 100 400 
p6| `s$,`s1 ,100 
q)`qty xgroup sp /直接使用xgroup关键字来进行分组
qty| s p 
---| -------------------------------
300| `s$`s1`s2`s4 `p$`p1`p1`p4 
200| `s$`s1`s1`s3`s4 `p$`p2`p4`p2`p2
400| `s$`s1`s2`s1 `p$`p3`p2`p5 
100| `s$`s4`s1 `p$`p5`p6 
q)ungroup `p xgroup sp /同样也可以使用ungroup进行解组,这里注意解组后的表的信息,将会按照一定的顺序排列
p s qty
---------
p1 s1 300
p1 s2 300
p2 s1 200
p2 s2 400
p2 s3 200
p2 s4 200
p3 s1 400
p4 s1 200
p4 s4 300
p5 s4 100
p5 s1 400
p6 s1 100

3) 有聚合的分组

我们也可以将分组后的结果进行聚合操作,最后的返回结果也是一个还有主键的表。

q)select sum c2 by c1 from t /使用by子句进行分组,并使用聚合函数sum
c1| c2
--| --
a | 40
b | 60
c | 50
q)t:([] desk:`a`b`a`b`a`b; acct:`1`2`3`4`1`4; pnl:1.1 -2.2 3.3 4.4 5.5 -.5)
q)t
desk acct pnl 
--------------
a 1 1.1 
b 2 -2.2
a 3 3.3 
b 4 4.4 
a 1 5.5 
b 4 -0.5
q)select ct:count desk, sum pnl by desk, acct from t /也可以指定多个用于分组的字段,同时也可以进行聚合操作
desk acct| ct pnl 
---------| -------
a 1 | 2 6.6 
a 3 | 1 3.3 
b 2 | 1 -2.2
b 4 | 2 3.9 
q)select by desk from t /这里可以直接省略子句,这样直接返回每个组的最后一条数据,这对金融行业也非常有用,比如可能需要查询最新的价格
desk| acct pnl 
----| ---------
a | 1 5.5 
b | 4 -0.5
q)t:([] c1:00:00:00.000+til 1000000; c2:1000000?`a`b; c3:1000000?100)
q)t
c1  c2 c3 
------------------------
00:00:00.000 a 39.27524
00:00:00.001 a 51.70911
00:00:00.002 a 51.59796
00:00:00.003 a 40.66642
00:00:00.004 b 17.80839
00:00:00.005 b 30.17723
00:00:00.006 a 78.5033 
00:00:00.007 a 53.47096
00:00:00.008 a  71.11716
00:00:00.009 a 41.1597 
00:00:00.010 a 49.31835
00:00:00.011 a 57.85203
00:00:00.012 b 8.388858
00:00:00.013 a 19.59907
00:00:00.014 a 37.5638 
00:00:00.015 b 61.37452
00:00:00.016 a 52.94808
00:00:00.017 b 69.16099
00:00:00.018 b 22.96615
00:00:00.019 a 69.19531
..
q)select avg c3 by 100 xbar c1,c2 from t /也可以使用带有表达式的分组操作,这里100 xbar表示以100的整数倍为步长增加
c1 c2| c3 
---------------| --------
00:00:00.000 a | 55.26494
00:00:00.000 b | 41.81758
00:00:00.100 a | 48.88826
00:00:00.100 b | 46.10946
00:00:00.200 a | 53.72272
00:00:00.200 b | 51.42873
00:00:00.300 a | 54.53996
00:00:00.300 b | 49.50472
00:00:00.400 a | 51.95785
00:00:00.400 b | 53.63795
00:00:00.500 a | 55.24681
00:00:00.500 b | 58.08108
00:00:00.600 a | 40.27698
00:00:00.600 b | 53.36846
00:00:00.700 a | 46.88679
00:00:00.700 b | 50.91821
00:00:00.800 a | 54.62589
00:00:00.800 b | 58.76686
00:00:00.900 a | 53.28107
00:00:00.900 b | 48.06001
..

4. exec关键字

我们知道select查询最后返回的是一张表,所以每个字段的行数是一样的,但是有时候可能想同时进行多个查询操作,最后每个子句可能行数不一样,这时就会报错,因此可以使用exec关键字来进行查询,其结果返回是一个列表或者字典

q)t:([] name:`a`b`c`d`e; state:`NY`FL`OH`NY`HI)
q)t
name state
----------
a NY 
b FL 
c OH 
d NY 
e HI 
q)select name, distinct state from t /由于name, distinct state两个结果行数不想等,因此查询失败
'length
q)exec name, distinct state from t /使用exec查询成功,结果返回的是一个字典
name | `a`b`c`d`e
state| `NY`FL`OH`HI
q)exec distinct state from t /对于单个子句其返回结果是一个列表
`NY`FL`OH`HI
q)select distinct state from t /select查询返回的结果是一张表
state
-----
NY 
FL 
OH 
HI

三、 表的数据更新

表的数据更新,我们使用update关键字来进行更新,跟新的语法模版如下:

update< pu > < by pb > from texp < where pw >

update的语法模板于select类似,只是< pu >为要更新的具体值。如果指定的列是表中存在的列,则更新该列;如果指定的列是表中不存在的列,则将该列添加到列表的末尾。其他子句于select一样。

q)t:([] c1:`a`b`c; c2:10 20 30)
q)t
c1 c2
-----
a 10
b 20
c 30
q)update c1:`x`y`z from t /更新的c1列在表中有,则更新对应的列
c1 c2
-----
x 10
y 20
z 30
q)t
c1 c2
-----
a 10
b 20
c 30
q)update c3:`x`y`z from t /更新的c3列在表中没有,则直接在表中添加新列,直接添加在表的右侧
c1 c2 c3
--------
a 10 x 
b 20 y 
c 30 z 
q)t /前面的更新并没有更新原表t,而是生成了一个新表
c1 c2
-----
a 10
b 20
c 30
q)update c3:`x`y`z from `t /要想更新原表t则需要传递表名的形式(`t)
`t
q)t
c1 c2 c3
--------
a 10 x 
b 20 y 
c 30 z 
q)update c2:c2+100 from t /也可以使用表达式的形式更新
c1 c2 c3
---------
a 110 x 
b 120 y 
c 130 z 
q)update c2+100 from t /使用表达式的第二种更新形式
c1 c2 c3
---------
a 110 x 
b 120 y 
c 130 z 
q)t:([] c1:`a`b`c; c2:10 20 30)
q)update c3:42 from t /更新的数据自动匹配所有行
c1 c2 c3
--------
a 10 42
b 20 42
c 30 42
q)t
c1 c2
-----
a 10
b 20
c 30
q)update c2:c2+100 from t where c1<>`a /使用where条件更新
c1 c2 
------
a 10 
b 120
c 130
q)update c3:1b from t where c2>15 /使用where进行条件更新
c1 c2 c3
--------
a 10 0 
b 20 1 
c 30 1 
q)t
c1 c2
-----
a 10
b 20
c 30
q)update c2:42 43 from t where c2>15 /使用where进行条件更新
c1 c2
-----
a 10
b 42
c 43
q)update c2:42 43 44 from t where c2>15 /由于更新的值与条件结果的行数不匹配,出现长度错误
'length
q)update c2:42 43 from t /由于更新的值与条件结果的行数不匹配,出现长度错误
'length
q)update c2:42.0 43 from t where c2>15 /由于更新的值与字段的类型不匹配,出现类型错误
'type
q)p
p | name color weight city 
--| -------------------------
p1| nut red 12 london
p2| bolt green 17 paris 
p3| screw blue 17 rome 
p4| screw red 14 london
p5| cam blue 12 paris 
p6| cog red 19 london
q)update avg weight by city from p /复合更新操作,包含分组,聚会操作
p | name color weight city 
--| -------------------------
p1| nut red 15 london
p2| bolt green 14.5  paris 
p3| screw blue 17 rome 
p4| screw red 15 london
p5| cam blue  14.5 paris 
p6| cog red 15 london

四、 表的数据删除

与SQL相似,表的删除为delete关键字,可以删除行和列。delete的删除语法如下:

delete< pcols > from texp < where pw >

< pcols >为表的列,texp为表的名称,< where pw >为删除的限制条件,这里需要注意,< pcols >和< where pw >不能同时存在。

q)t:([] c1:`a`b`c; c2:10 20 30)
q)t
c1 c2
-----
a 10
b 20
c 30
q)delete c1 from t /删除c1列从表中。这里注意会将整个c1列删除,包括列名
c2
--
10
20
30
q)t
c1 c2
-----
a 10
b 20
c 30
q)delete from t where c2>15 /条件删除
c1 c2
-----
a 10
q)delete from t where c2=30 /条件删除
c1 c2
-----
a 10
b 20
q)t /查询表t发现所有的内容都还在,因此删除原表需要传递名称的形式
c1 c2
-----
a 10
b 20
c 30
q)delete c1 from `t /通过传递名称(`t)形式在原表中删除
`t
q)t
c2
--
10
20
30
q)t:([] c1:1 2; c2:`a`b; c3:1.1 2.2; c4:2015.01.01 2015.01.02)
q)t
c1 c2 c3 c4
--------------------
1 a 1.1 2015.01.01
2 b 2.2 2015.01.02
q)(select c1,c2,c4 from t)~(delete c3 from t) /可以发现者两条结果是一样的,因此我们可以充分利用标的查询与删除的特性
1b
q)select c1,c2,c4 from t 
c1 c2 c4
----------------
1 a 2015.01.01
2 b 2015.01.02
q)delete c3 from t 
c1 c2 c4
----------------
1 a 2015.01.01
2 b 2015.01.02

你可能感兴趣的:(Q语言——表的高级查询语言(一))