一、 表的排序
我们知道表的字段中的值其实是由列表组成,因此具有固有的顺序。但是我们也可以进行排序操作的,只是Q语言中没有order by的子句,
我们可以直接使用xasc和xdesc,同时两个也可以同时使用进行混合排序。
q)t:([] c1:`a`b`c`a; c2:20 10 40 30)
q)t
c1 c2
-----
a 20
b 10
c 40
a 30
q)`c2 xasc t /给表t进行升序排序
c1 c2
-----
b 10
a 20
a 30
c 40
q)`c1`c2 xasc t /给表t中的c1列和c2列同时进行升序排序,但是不是分开单独进行排序,而是联合起来排序
c1 c2
-----
a 20
a 30
b 10
c 40
q)t /此时我们发现原表t并没有排序,因此表的操作要改表原表都是需要传递表的名称(`table_name)的形式来操作
c1 c2
-----
a 20
b 10
c 40
a 30
q)`c1`c2 xasc `t /传递表的名称的形式来排序,返回的结果是表的名称
`t
q)t
c1 c2
-----
a 20
a 30
b 10
c 40
q)`c1`c2 xdesc t /给表t中的c1列和c2列同时进行降序排序,但是不是分开单独进行排序,而是联合起来排序
c1 c2
-----
c 40
b 10
a 30
a 20
q)t
c1 c2
-----
a 20
a 30
b 10
c 40
q)`c1 xasc `c2 xdesc t /同时也可以对表进行升序和降序排列操作,但是注意并不是将两个列单独,也是联合排序,有点类似于分组排序一样
c1 c2
-----
a 30
a 20
b 10
c 40
二、 表的重命名
有时候我们可能需要对一个表的字段名进行重新命名,字段的名称重新命名的语法如下:
@[cols[table_name]; where cols[table_name]=
old_name; :;
new_name] xcol table_name
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)@[cols[t]; where cols[t]=`c2; :; `new2] xcol t /将字段c2的列名改为new2,注意这里的冒号是必要的,相当于整个的意思
c1 new2 c3
-----------
a 10 1.1
b 20 2.2
c 30 3.3
q)@[cols[t]; where cols[t] in `c1`c3; :; `new1`new3] xcol t /当然也可以同时修改多个字段的名称
new1 c2 new3
------------
a 10 1.1
b 20 2.2
c 30 3.3
q)`new1`new2 xcol t /当然如果修改的字段名是从左往右依次修改的,则可以使用这种简便的形式
new1 new2 c3
-------------
a 10 1.1
b 20 2.2
c 30 3.3
三、 表的联接查询
关系数据库设计的本质是使用关系和键对数据进行规范化,然后使用连接进行重组。规范化消除了重复数据,这占用了空间并且难以保持一致。联接恢复原始的扁平矩形形式,
使数据易于使用(电子表格如此受欢迎的原因)。
在SQL中,具有主键和外键的关系结构是静态的。必须先使用单独的语言(DDL)定义数据才能使用数据。必须预先建立外键/主键关系才能进行连接。
在Q中,表是该语言中的第一类实体。你可以静态定义表和关系,也可以轻松地动态创建它们。甚至可以连接可能具有关系但不具有关系的表。
联接可以分为内部或外部。内联仅对具有匹配的两个操作数中的值进行配对。外联分为左外联和右外联。
大多数连接是equijoins,意味着键必须相等。在Q也有非等值连接,称为*as of *joins,其中一个键被用于测试低于或-等于针对另一个表中的键。
1. 隐式联接查询
隐式联接的操作语法如下:
select cold,k.colm from d
其中cold,k.colm为表d和表m中的相关列。
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)select sname:s.name,qty from sp /查询sp表的s字段和qty字段,同时联接到s表中的name字段返回对应的名称,这时我们看到返回的结果中sp中的s字段与s表中的name字段
/通过主键和外键的关系进行了匹配后返回对应的name
sname qty
---------
smith 300
smith 200
smith 400
smith 200
clark 100
smith 100
jones 300
jones 400
blake 200
clark 200
clark 300
smith 400
q)select sname:s.name, pname:p.name, qty from sp /与上面类似,此次查询多了p字段的联接查询
sname pname qty
---------------
smith nut 300
smith bolt 200
smith screw 400
smith screw 200
clark cam 100
smith cog 100
jones nut 300
jones bolt 400
blake bolt 200
clark bolt 200
clark screw 300
smith cam 400
q)emaster:([eid:1001 1002 1003 1004 1005] currency:`gbp`eur`eur`gbp`eur)
q)update eid:`emaster$1001 1002 1005 1004 1003 from `s /将emaster表中的eid列添加到表s中,并且作为表s的一个外键
`s
q)emaster
eid | currency
----| --------
1001| gbp
1002| eur
1003| eur
1004| gbp
1005| eur
q)meta s /查看表s的结构信息
c | t f a
------| -----------
s | s
name | s
status| j
city | s
eid | j emaster
q)s
s | name status city eid
--| ------------------------
s1| smith 20 london 1001
s2| jones 10 paris 1002
s3| blake 30 paris 1005
s4| clark 20 london 1004
s5| adams 30 athens 1003
q)select s.name, qty, s.eid.currency from sp /我们可以联接到表s的eid列,再通过eid列联接到表emaster的currency列
name qty currency
------------------
smith 300 gbp
smith 200 gbp
smith 400 gbp
smith 200 gbp
clark 100 gbp
smith 100 gbp
jones 300 eur
jones 400 eur
blake 200 eur
clark 200 gbp
clark 300 gbp
smith 400 gbp
2. 临时左联查询(lj)
lj联接对于有外键的表的操作非常有效,同时对于操作的两个联接的表如果没有外键关系,也可以进行联接查询操作。lj操作语法如下:
left_table lj right_table
right_table是一个含有主键的表(目标),left_table是一个普通表或含有主键的表(源),left_table具有right_table的外键或与right_table表中名称和类型匹配的列。
结果是left_table的所有记录和增加了匹配上的right_table的记录。对于left_table中没有的记录,会用空值代替。
q)t:([] k:1 2 3 4; c:10 20 30 40)
q)t
k c
----
1 10
2 20
3 30
4 40
q)kt:([k:2 3 4 5]; v:200 300 400 500)
q)kt
k| v
-| ---
2| 200
3| 300
4| 400
5| 500
q)t lj kt /左联接查询操作,可以看出返回的结果包含了t表的所有数据,增加了kt表中的v列(该列在t表中不存在),并且由于kt表中的k列中没有1这条记录,因此对应的v列用空值代替
k c v
--------
1 10
2 20 200
3 30 300
4 40 400
q)kt:([k:1 2 3 4 5]; v1:10 20 30 40 50; v2:1.1 2.2 3.3 4.4 5.5)
q)kt
k| v1 v2
-| ------
1| 10 1.1
2| 20 2.2
3| 30 3.3
4| 40 4.4
5| 50 5.5
q)tf:([] k:`kt$1 2 3 4; c:10 20 30 40) /表tf有一个外键,为kt表的k字段
q)meta tf
c| t f a
-| ------
k| j kt
c| j
q)tf
k c
----
1 10
2 20
3 30
4 40
q)tf lj kt /通过左联操作查询,返回了tf表的所有记录,表tf的k字段与kt表的k字段匹配后对应的记录
k c v1 v2
-----------
1 10 10 1.1
2 20 20 2.2
3 30 30 3.3
4 40 40 4.4
q)select k,c,k.v1,k.v2 from tf /tf lj kt操作查询与select k,c,k.v1,k.v2 from tf的结果是一样的,因此通过lj操作更加快捷简便
k c v1 v2
-----------
1 10 10 1.1
2 20 20 2.2
3 30 30 3.3
4 40 40 4.4
q)t:([] k:1 2 3 4; v:10 20 30 40)
q)kt:([k:2 3 4 5]; v:200 300 400 500)
q)t
k v
----
1 10
2 20
3 30
4 40
q)kt
k| v
-| ---
2| 200
3| 300
4| 400
5| 500
q)t lj kt /当left_table和right_table具有重复的非主键列时(该操作具有upsert意义),right_table(目标)列中的值优先于left_table(源)中的值。
k v
-----
1 10
2 200
3 300
4 400
3. 临时内联查询(ij)
ij联接与lj联接非常相似,同时对于操作的两个联接的表如果没有外键关系,也可以进行联接查询操作。ij操作语法如下:
left_table ij right_table
right_table是一个含有主键的表(目标),left_table是一个普通表或含有主键的表(源),left_table具有right_table的外键或与right_table表中名称和类型完全匹配的列。
返回的结果包含沿着共同的字段的记录(或right_table主键与left_table相同匹配上的记录)。
q)t:([] k:1 2 3 4; c:10 20 30 40)
q)kt:([k:2 3 4 5]; v:2.2 3.3 4.4 5.5)
q)t
k c
----
1 10
2 20
3 30
4 40
q)kt
k| v
-| ---
2| 2.2
3| 3.3
4| 4.4
5| 5.5
q)t ij kt /ij联接查询,返回的结果为kt中的主键k字段与t表中的k字段匹配后返回的对应的记录(主要是k字段匹配)
k c v
--------
2 20 2.2
3 30 3.3
4 40 4.4
q)t:([] k:1 2 3 4; v:10 20 30 40)
q)kt:([k:2 3 4 5]; v:200 300 400 500)
q)t
k v
----
1 10
2 20
3 30
4 40
q)kt
k| v
-| ---
2| 200
3| 300
4| 400
5| 500
q)t ij kt /对于ij查询操作,除了匹配的字段外还有其他的相同字段时,则相当于upsert操作,right_table会去更新left_table中的记录
k v
-----
2 200
3 300
4 400
q)kt1:([k:1 2 3 4]; v:10 0N 30 40)
q)kt:([k:2 3 4 5]; v:200 300 400 500)
q)kt1 ij kt
k| v
-| ---
2| 200
3| 300
4| 400
4. 等值联接查询(ej)
我们可能有时候联接查询的两张表都不含有主键或者外键,这时我们通过隐式联接或者内联会出现类型不匹配的错误,如:
q)t1:([] k:1 2 3 4; c:10 20 30 40)
q)t2:([] k:2 2 3 4 5; c:200 222 300 400 500; v:2.2 22.22 3.3 4.4 5.5)
q)t1
k c
----
1 10
2 20
3 30
4 40
q)t2
k c v
-----------
2 200 2.2
2 222 22.22
3 300 3.3
4 400 4.4
5 500 5.5
q)t1 ij t2 /t1表与t2表进行ij联接,出现类型错误,隐式联接和内联都要求right_table都必须是还有主键的表
'type
因此我们可以通过等值联接(ej)来进行联接查询操作。等值联接的查询方式如下:
ej[`table_colmn; table1; table2]
ej联接查询的三个参数,第一个为指定table1和table2都含有的字段名,table1和table2为两个指定的表。与隐式联接或内联一样一样,对于重复的列相当于upsert操作,
table2的值会upsert到table1上。
q)ej[`k; t1; t2] /这里需要注意的是t2中的k有两条记录都是2,因此最终的结果会匹配到两个2的值,但是内联就只会出现一个2的记录
k c v
-----------
2 200 2.2
2 222 22.22
3 300 3.3
4 400 4.4
q)ej[`k; t2; t1]
k c v
----------
2 20 2.2
2 20 22.22
3 30 3.3
4 40 4.4
q)t1 ij `k xkey t2 /`k xkey t2为将t2表的k字段指定为主键,再进行内联,会发现k字段中只有2这一条记录
k c v
---------
2 200 2.2
3 300 3.3
4 400 4.4
5. 加联接查询(pj)
加联接可以看作是一个左联接,会将重复的记录进行相加,这就要求重复的记录必须是数字类型中的一种。当你有两个非键列均为数字的相同模式的表时,这非常有用。
q)t:([] k:`a`b`c; a:100 200 300; b:10\. 20\. 30.; c:1 2 3)
q)kt:([k:`a`b] a:10 20; b:1.1 2.2)
q)t pj kt
k a b c
------------
a 110 11.1 1
b 220 22.2 2
c 300 30 3
6. 合并联接查询(uj)
合并联接查询(uj)更强大的是它可以垂直组合两个或多个任意类型的表,但是只能是统一类型的表,如不能将普通表与主键表进行联合查询。其查询语句如下:
left_table uj right_table
(uj/)(table_one;……;table_n)
右操作数中的记录会以如下的方式添加到左操作数中的记录中。对于右操作数存在而左操作数中不存在的列,结果表会用相同的名称和类型的新列作为扩展。结果中的初始记录来源于左操作数,
新列会设为null。右操作数的记录的字段值在相应的列下。一个例子胜过千言。
q)t1:([] c1:`a`b`c; c2: 10 20 30)
q)t2:([] c1:`x`y; c3:8.8 9.9)
q)t1
c1 c2
-----
a 10
b 20
c 30
q)t2
c1 c3
------
x 8.8
y 9.9
q)t1 uj t2 /表的联合查询,相同字段会直接追加,不同的字段会在右侧添加为新字段,没有的数据用空值代替
c1 c2 c3
---------
a 10
b 20
c 30
x 8.8
y 9.9
q)t1:([] c1:`a`b`c; c2: 10 20 30)
q)t2:([] c1:`a`b`c; c2: 10 20 30)
q)t1 uj t2 /对于不含主键的表,两个表的uj联合查询不会进行重复记录匹配,对于相同的字段,会直接在表的后面追加
c1 c2
-----
a 10
b 20
c 30
a 10
b 20
c 30
q)t3:([] c1:`e`f`g; c2:50 60 70; c3:5.5 6.6 7.7)
q)t3
c1 c2 c3
---------
e 50 5.5
f 60 6.6
g 70 7.7
q)(uj/)(t1;t2;t3) /多个表的联合查询
c1 c2 c3
---------
a 10
b 20
c 30
x 8.8
y 9.9
e 50 5.5
f 60 6.6
g 70 7.7
q)kt1:([k:1 2 3] v1:`a`b`c; v2:10 20 30)
q)kt2:([k:3 4] v2:300 400; v3:3.3 4.4)
q)kt1
k| v1 v2
-| -----
1| a 10
2| b 20
3| c 30
q)kt2
k| v2 v3
-| -------
3| 300 3.3
4| 400 4.4
q)kt1 uj kt2 /两个含有主键的表的联合查询,对于有相同主键的记录,右边的表会更新左边表中对应的数据
k| v1 v2 v3
-| ----------
1| a 10
2| b 20
3| c 300 3.3 /仔细注意这条数据的更新
4| 400 4.4
q)t1:([] c1:`a`b`c; c2: 10 20 30)
q)kt1:([k:1 2 3] v1:`a`b`c; v2:10 20 30)
q)t1 uj kt1 /不同类型的表进行联合查询直接报错
'nyi
7. 非等值联接查询(aj与asof)
as-of如此命名是因为人们经常沿时间列查询另外一个表中较新的值(如在拍卖中,一件商品会有多个报价,但是最终会只有一个交易价格;或者一直股票在收盘前会有多个价格,
但是收盘时只会有一个价格。那有时候我们就想知道在最终交易价格之前的报价会是多少)。因此as-of联接不是等值联接,而是匹配小于或者等于当前时间的记录。aj的语法如下:
aj[
c 1 ...
c n ; t 1 ; t 2 ]
其中t 1和*t 2 要联接的表,c 1 ...
c n *为两个表中共有的字段。这里不要求要联接的列为主键,但是如果是的话,速度会更快。返回的结果中包含了两个表中的所有的字段。
aj语义为:除了最后一列,所有指定的字段的匹配都是基于等值匹配。假设在两个表中都存在一个cn这个字段,给定一个 t1 中的cn,就会在t2中根据小于或等于给定值匹配选择一个最大值。
尤其是,当cn列作为时间序列值时,对于于t1中的每个cn值,匹配选择其cn的t2值在“ t1 ”最新的一条记录。
我们可以看如下的例子:
q)show t:([] ti:10:01:01 10:01:03 10:01:04;sym:`msft`ibm`ge;qty:100 200 150) /交易表t
ti sym qty
-----------------
10:01:01 msft 100
10:01:03 ibm 200
10:01:04 ge 150
q)show q:([] ti:10:01:00 10:01:01 10:01:01 10:01:02;sym:`ibm`msft`msft`ibm;px:100 99 101 98) /报价表q
ti sym px
-----------------
10:01:00 ibm 100
10:01:01 msft 99
10:01:01 msft 101
10:01:02 ibm 98
q)aj[`sym`ti;t;q]
ti sym qty px
---------------------
10:01:01 msft 100 101
10:01:03 ibm 200 98
10:01:04 ge 150
/aj[`sym`ti;t;q]的执行为:我们从t表中第一条记录开始。寻找`msft与交易时间10:01:01(这时交易价格为100) 匹配的报价记录,有两个这样的记录,
/都标记为10:01:01,但最后一个记录的价格为101,因此这时在这个时刻的最新报价就是101。接下来我们查找`ibm,但是我们发现截至10:01:03这个时刻,
/`ibm的最新报价在10:01:02 这个时间点,因此最新报价就是98。最后就是`ge在10:01:04 没有匹配的报价记录。
q)aj0[`sym`ti;t;q] /对于aj0,在时间上会选择最新的报价时间,而不是交易时间,因此对于`ibm的最新报价时间是10:01:02,而不是交易时间10:01:03
ti sym qty px
---------------------
10:01:01 msft 100 101
10:01:02 ibm 200 98
10:01:04 ge 150
q)t:([] ti:10:01:01 10:01:03 10:01:04; sym:`msft`ibm`ge; qty:100 200 150; px:45 160 55)
q)t
ti sym qty px
---------------------
10:01:01 msft 100 45
10:01:03 ibm 200 160
10:01:04 ge 150 55
q)t asof `sym`ti!(`ibm;10:01:03) /我们也可以使用asof来直接查询某个品牌在某个记录的交易价格与报价,如果给定的是字典的形式,最后返回的结果也是字典,如果给定的是表的形式,最后返回的结果也是表
qty| 200
px | 160
q)t asof ([] sym:`msft`ibm; ti:10:01:01 10:01:03)
qty px
-------
100 45
200 160
q)promotions:([] name:`nuba`devi`nuba`devi`nuba;dt:2000.01.01 2005.02.14 2010.02.01 2012.08.12 2015.11.01;title:`associate`analyst`director`cfo`retired)
q)promotions
name dt title
-------------------------
nuba 2000.01.01 associate
devi 2005.02.14 analyst
nuba 2010.02.01 director
devi 2012.08.12 cfo
nuba 2015.11.01 retired
q)promotions asof `name`dt!(`nuba; 2009.07.04) /asof的还有一个重要用处就是,当给定的某个时间记录,如果当前时间记录没有具体值,则会去匹配这个时间点之前的记录值,
/这里`nuba在2009.07.04的职位是associate
title| associate
q)promotions asof `name`dt!(`devi; 2015.12.01) /同样`devi在2015.12.01之前的最新职位是cfo
title| cfo
q)events:([] name:`nuba`devi; dt: 2009.07.04 2015.12.01)
q)events
name dt
---------------
nuba 2009.07.04
devi 2015.12.01
q)aj[`name`dt; events; promotions] /我们也可以通过aj的形式来进行查询操作
name dt title
-------------------------
nuba 2009.07.04 associate
devi 2015.12.01 cfo
8. 窗口联接查询(wj)
窗口连接查询(wj)是对非等值联接查询(aj)的概括,特别适用于分析财务中交易和报价之间的关系。比如你想知道每笔交易的前后报价是多少。例如,
要确定一个交易的结果情况,则需要检查交易时间内的买入价和卖出价的范围。自己编写这样的查询会很麻烦。所以Q提供了内置的窗口连接查询(wj),可以计算每个交易周围的间隔。
其查询语法如下:
wj[w;c;t;(q;(f0;c0);(f1;c1))]
其中w是一个查询范围,例如交易的前10秒到交易后5秒;c是需要进行匹配的字段名称;t是交易表的名称;(q;(f0;c0);(f1;c1))是对剩余字段的一个聚合等操作,如找最大的报价和最低的交易价格。
q)show t:([]sym:3#`aapl;time:09:30:01 09:30:04 09:30:08;price:100 103 101) /一个交易表t,交易表在时间上具有唯一性
sym time price
-------------------
aapl 09:30:01 100
aapl 09:30:04 103
aapl 09:30:08 101
q)show q:([] sym:8#`aapl;time:09:30:01+(til 5),7 8 9;ask:101 103 103 104 104 103 102 100;bid:98 99 102 103 103 100 100 99) /报价表q
sym time ask bid
---------------------
aapl 09:30:01 101 98
aapl 09:30:02 103 99
aapl 09:30:03 103 102
aapl 09:30:04 104 103
aapl 09:30:05 104 103
aapl 09:30:08 103 100
aapl 09:30:09 102 100
aapl 09:30:10 100 99
q)w:-2 1+\:t `time /设置窗口宽度,这里的含义是将t表的`time字段的每个时间往前2秒,往后1秒,得到一个时间列表
q)w /得到的时间列表,我们需要竖着理解,如t表中09:30:01的推导时间是09:29:59和09:30:02
09:29:59 09:30:02 09:30:06
09:30:02 09:30:05 09:30:09
q)c:`sym`time /需要进行匹配的字段
q)c
`sym`time
q)wj[w;c;t;(q;(::; `ask);(::; `bid))] /这里没有进行聚合操作,::的含义是将`ask和`bid字段所有窗口时间内的记录都选择。对于窗口时间未完全匹配上的,就会像aj查询操作一样,
/去寻找当前时间点的之前的最新的记录,如09:30:08 101的窗口时间是09:30:06和09:30:09,而没有09:30:06这条时间记录,因此就会去匹配09:30:06之前的最新记录09:30:05的数据
sym time price ask bid
--------------------------------------------------
aapl 09:30:01 100 101 103 98 99
aapl 09:30:04 103 103 103 104 104 99 102 103 103
aapl 09:30:08 101 104 103 102 103 100 100
q)wj[w;c;t;(q;(max; `ask);(min; `bid))] /这里我们对q表匹配后的数据进行了聚合操作,如返回窗口时间内的`ask字段的最大值和`bid字段的最小值
sym time price ask bid
---------------------------
aapl 09:30:01 100 103 98
aapl 09:30:04 103 104 99
aapl 09:30:08 101 104 100
q)wj1[w; c; t; (q; (::; `ask); (::; `bid))] /wj1的窗口匹配机制与wj的匹配机智有一些不同,wj1只会匹配窗口时间内的,对于窗口时间未完全匹配上的,会去寻找当前时间点内的记录,
/如09:30:08 101的窗口时间是09:30:06和09:30:09,而没有09:30:06这条时间记录,因此就会去匹配09:30:06内的最新记录09:30:08的数据
sym time price ask bid
--------------------------------------------------
aapl 09:30:01 100 101 103 98 99
aapl 09:30:04 103 103 103 104 104 99 102 103 103
aapl 09:30:08 101 103 102 100 100
四、 表的参数化查询
关系型数据库具有存储过程,这些过程是包含SQL语句的数据库语言。而编程语言不是SQL标准的一部分。而且不同语言之间存在显着差异,但通常是第三代命令式编程语言。
这种情况迫使程序员做出选择:学习专有语言以将某些功能放在数据上,或者将数据提取到应用程序服务器中以执行计算。各种多层体系结构已经发展到解决这个问题,但它们会增加系统成本和复杂性。
这种分离在kdb +中被消除,因为Q是存储过程语言,并且它具有处理大数据的能力和性能。在表上运行的任何Q函数实际上都是存储过程。函数参数可用于为查询提供特定值。
特别是,可以在具有参数的函数内调用select,exec,update和delete模板,以生成参数化查询。
q)t:([] c1:`a`b`c; c2:10 20 30; c3:1.1 2.2 3.3)
q)select from t where c2>15 /使用标准select语句查询
c1 c2 c3
---------
b 20 2.2
c 30 3.3
q)proc:{[sc] select from t where c2>sc} /我们可以将select标准查询语句放在函数当中,后续相同的查询操作我们只需要传递参数即可
q)proc 15 /函数的参数化查询
c1 c2 c3
---------
b 20 2.2
c 30 3.3
q)proc1:{[nms;sc] select from t where c1 in nms, c2>15} /函数化复杂的select条件查询
q)proc1[`a`c;15] /函数式查询
c1 c2 c3
---------
c 30 3.3
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)proc2:{[t;nms;delta] update c2+delta from t where c1 in nms} /我们传递的参数也可以是针对不同的表
q)proc2[t;`a`c;100]
c1 c2 c3
----------
a 110 1.1
b 20 2.2
c 130 3.3
q)t
c1 c2 c3
---------
a 10 1.1
b 20 2.2
c 30 3.3
q)proc2[`t;`a`c;100] /同样需要改变原始表格的数据,我们也是需要传递表名的形式(`table_name)
`t
q)t
c1 c2 c3
----------
a 110 1.1
b 20 2.2
c 130 3.3
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)procf:{[cs] cs#t} /多字段的查询方式
q)procf[`c1`c2]
c1 c2
-----
a 10
b 20
c 30
五、 视图
SQL视图实际上是一个查询表达式,其结果集可以像表一样使用。视图对于封装数据非常有用,例如隐藏列或简化复杂查询。
Q-sql 视图是使用双冒号运算符创建的别名的命名表表达式。在视图中使用模板很常见。
q)t:([] c1:`a`b`c; c2:10 20 30)
q)t
c1 c2
-----
a 10
b 20
c 30
q)u:select from t where c2>15 /将查询语句赋给一个变量
q)v::select from t where c2>15 /创建一个视图
q)u
c1 c2
-----
b 20
c 30
q)v
c1 c2
-----
b 20
c 30
q)view `v /查看视图的内容
"select from t where c2>15"
q)update c2:15 from `t where c1=`b /更新表的内容
`t
q)u /这时我们发现通过变量u查询,并没有得到更新的内容
c1 c2
-----
b 20
c 30
q)v /视图的方式查询能够同步得到更新的内容
c1 c2
-----
c 30
q)a:42
q)b::a /这样实际上是前面讲的别名,a的值改变会同步到b的值也改变
q)views `. /查询当前内存中有哪些视图(或别名)
`b`v
六、 函数式查询
我们知道Q语言是一种高度抽象的概括语言,与其他编程语言的风格非常不同,同时该编程语言的语句信息密度非常的大,因此学起来有一定的难度,但是当你掌握之后,你会发现它是如此的优美与简洁。
前面介绍的表的增删改查,Q语言还可以通过更加简洁的形式来查询,那就是函数式查询,其查询的语法模板如下:
?[t;c;b;a] / select 与 exec查询操作方式
![t;c;b;a] / update 与 delete查询操作方式
传递的参数不同,最后查询的结果可能就不同,通过重载的形式来进行处理,这里t、c、b、a的含义分别是:
t——>table_name
c——>查询的限制条件,相当于where
b——>分组的限制条件,0b一般表示没有分组,相当于by
a——>聚合类的函数,()空列表表示没有限制条件或聚合操作
q)t:([] c1:`a`b`a`c`a`b`c; c2:10*1+til 7; c3:1.1*1+til 7)
q)ft:{([] c1:`a`b`a`c`a`b`c; c2:10*1+til 7; c3:1.1*1+til 7)} /ft为函数,内容为创建一个表
1. select查询
q)t
c1 c2 c3
---------
a 10 1.1
b 20 2.2
a 30 3.3
c 40 4.4
a 50 5.5
b 60 6.6
c 70 7.7
q)ft
{([] c1:`a`b`a`c`a`b`c; c2:10*1+til 7; c3:1.1*1+til 7)}
q)select from t
c1 c2 c3
---------
a 10 1.1
b 20 2.2
a 30 3.3
c 40 4.4
a 50 5.5
b 60 6.6
c 70 7.7
q)?[t; (); 0b; ()] /该查询操作与select from t的查询操作是一样的,这里c为空列表,b为0b表示没有分组,a为空列表表示没有聚合操作
c1 c2 c3
---------
a 10 1.1
b 20 2.2
a 30 3.3
c 40 4.4
a 50 5.5
b 60 6.6
c 70 7.7
q)?[`t; (); 0b; ()] /传递表时,通过传递`table_name的形式在原始表上进行相应的操作
c1 c2 c3
---------
a 10 1.1
b 20 2.2
a 30 3.3
c 40 4.4
a 50 5.5
b 60 6.6
c 70 7.7
q)?[ft[]; (); 0b; ()] ] /传递表时我们也可以传递函数表
c1 c2 c3
---------
a 10 1.1
b 20 2.2
a 30 3.3
c 40 4.4
a 50 5.5
b 60 6.6
c 70 7.7
q)select from t where c2>35, c1 in `b`c
c1 c2 c3
---------
c 40 4.4
b 60 6.6
c 70 7.7
q)?[t; ((>; `c2; 35); (in; `c1; enlist `b`c)); 0b; ()] /这里的查询操作与select from t where c2>35, c1 in `b`c是一样的,在c中,对于多个限制条件,
/我们将每一个限制条件单独写成前缀的形式(>; `c2; 35),最后将多个限制条件组合成一个列表
c1 c2 c3
---------
c 40 4.4
b 60 6.6
c 70 7.7
q)select max c2, c2 wavg c3 from t
c2 c3
------
70 5.5
q)?[t; (); 0b; `maxc2`wtavg!((max; `c2); (wavg; `c2;`c3))] /这里的查询操作与select max c2, c2 wavg c3 from t是一样的,在a中,我们需要将聚合函数写成字典的形式,
/每一个聚合操作为一个字典的值,字典的key不限制,但是尽量不要与表的字段名相同
maxc2 wtavg
-----------
70 5.5
q)select by c1 from t
c1| c2 c3
--| ------
a | 50 5.5
b | 60 6.6
c | 70 7.7
q)?[t; (); (enlist `c1)!enlist `c1; ()] /这里的查询操作与select by c1 from t是一样的,这里添加了分组查询,因此b不再是0b了,分组查询也是一样,我们需要将分的组写成字典的形式,
/对于单个分组的字段,注意使用enlist
c1| c2 c3
--| ------
a | 50 5.5
b | 60 6.6
c | 70 7.7
q)?[t; (); `c1`c2!(`c1;`c2); ()] /多个分组的字段查询操作
c1 c2| c3
-----| ---
a 10| 1.1
a 30| 3.3
a 50| 5.5
b 20| 2.2
b 60| 6.6
c 40| 4.4
c 70| 7.7
q)select max c2, c2 wavg c3 by c1 from t where c2>35, c1 in `b`c
c1| c2 c3
--| ------
b | 60 6.6
c | 70 6.5
q)c:((>; `c2; 35); (in; `c1; enlist `b`c)) /where子句,多个限制条件用前缀形式,并组合成列表
q)b:(enlist `c1)!enlist `c1 /分组子句,写成字典的形式
q)a:`maxc2`wtavg!((max; `c2); (wavg; `c2; `c3)) /聚合函数,也是用前缀形式,最后写成字典的形式
q)?[t;c;b;a]
c1| maxc2 wtavg
--| -----------
b | 60 6.6
c | 70 6.5
q)t:([] c1:`a`b`c`a; c2:10 20 30 40)
q)t
c1 c2
-----
a 10
b 20
c 30
a 40
q)select[>c1] c1,c2 from t /select查询的排序
c1 c2
-----
c 30
b 20
a 10
a 40
q)?[t; (); 0b; `c1`c2!`c1`c2; 0W; (>:; `c1)] /函数查询的一种扩展形式,多了两个参数,第一个参数是后面的排序操作的用来比较大小的初始值,我们选择0W,第二个为列表,表示将整个c1字段进行排序
c1 c2
-----
c 30
b 20
a 10
a 40
2. exec查询
exec查询操作与select查询操作基本相似,只是当没有分组时b的参数不再是0b,而是一个空列表(),同时返回的结果取决于传递的参数形式,如果为列表形式返回结果为列表,
如果为字典的形式,返回结果就是字典
q)t:([] c1:`a`b`c`a; c2:10 20 30 40; c3:1.1 2.2 3.3 4.4)
q)t
c1 c2 c3
---------
a 10 1.1
b 20 2.2
c 30 3.3
a 40 4.4
q)exec distinct c1 from t
`a`b`c
q)?[t; (); (); (distinct; `c1)] /这里的a参数为列表的形式,因此返回的结果是一个列表
`a`b`c
q)exec c1:distinct c1 from t
c1| a b c
q)?[t; (); (); (enlist `c1)!enlist (distinct; `c1)] /这里的a参数为字典的形式,因此返回的结果是一个字典
c1| a b c
q)exec distinct c1,c2 from t
c1| `a`b`c
c2| 10 20 30 40
q)?[t; (); (); `c1`c2!((distinct; `c1); `c2)] /这里的a参数为字典的形式,因此返回的结果是一个字典
c1| `a`b`c
c2| 10 20 30 40
q)exec c2 by c1 from t
a| 10 40
b| ,20
c| ,30
q)t
c1 c2 c3
---------
a 10 1.1
b 20 2.2
c 30 3.3
a 40 4.4
q)?[t; (); `c1; `c2] /对单个字段进行分组的查询操作形式
a| 10 40
b| ,20
c| ,30
3. update查询
update查询操作形式与前面介绍的select有一点差别,同时?被!取代。
q)t:([] c1:`a`b`c`a`b; c2:10 20 30 40 50)
q)t
c1 c2
-----
a 10
b 20
c 30
a 40
b 50
q)update c2:100 from t where c1=`a
c1 c2
------
a 100
b 20
c 30
a 100
b 50
q)c:enlist (=; `c1; enlist `a) /这里的c为修改的限制条件,一样用前缀的形式,同时注意对于单个限制条件要写成enlist的形式
q)b:0b /0b表示不进行分组
q)a:(enlist `c2)!enlist 100 /a为要update的值
q)![t;c;b;a]
c1 c2
------
a 100
b 20
c 30
a 100
b 50
q)update c2:sums c2 by c1 from t
c1 c2
-----
a 10
b 20
c 30
a 50
b 70
q)c:()
q)b:(enlist `c1)! enlist `c1 /这里对c1字段进行分组,然后累加分组后的c2字段的值
q)a:(enlist `c2)! enlist(sums; `c2) /累加操作
q)c
q)b
c1| c1
q)a
c2| +\ `c2
q)![t; c; b; a]
c1 c2
-----
a 10
b 20
c 30
a 50
b 70
4. delete查询
delete操作也是与前面类似,但是这里需要注意的是c和a参数是至少要有两者中的一个,如果c存在,则指定要删除的行;如果a存在,则它是要删除的字段名称。
在c存在的情况下,必须指定a为空符号列表。同时b的参数始终为0b,因为没有分组删除。
q)t:([] c1:`a`b`c`a`b; c2:10 20 30 40 50)
q)t
c1 c2
-----
a 10
b 20
c 30
a 40
b 50
q)delete from t where c1=`b
c1 c2
-----
a 10
c 30
a 40
q)![t; enlist (=; `c1; enlist `b); 0b; `symbol$()] /c为限制条件,同时a要将列表转换为symbol形式
c1 c2
-----
a 10
c 30
a 40
q)delete c2 from t
c1
--
a
b
c
a
b
q)![t; (); 0b; enlist `c2] /c不存在,a的参数为指定删除的字段名称
c1
--
a
b
c
a
b