Functional queries

I used to hate functional queries. Part of me still does. They are hard to write, look like gibberish and not as simple to understand as qsql queries. I can’t believe I am saying this but as you get more familiar with q, you start seeing their advantages and point of their existence. q is a functional language and the best way to unlock its full potential is via functions.

For me, functional queries are most useful when I am trying to run them on a remote process/db. Instead of wrapping a qsql query in string and then sending it via IPC, I prefer to use functional queries. This is especially important when you want to pass arguments to a query…yes, basically treating it as a function. That is when having a functional query helps because it allows you to easily pass locally defined parameters to a remote process. Anyways, that’s a discussion for another time. Today, we will look at how to actually write them.

For our examples, we will look at this sample trade table:

t:([];time:5?.z.t;sym:5?`AAPL`MSFT`IBM;price:5?100;size:5?200)

A functional query consists of 4 values:
t – table
c – constraints
b – group by
a – aggregate

If we were to dissect a qsql query:

select minP:min price by sym from t where time>04:00
t - t
c - where time>04:00
b - sym
a - minP:min price

So far so good? Let’s look at different types of functional queries.

Select

Syntax for a select query is:

?[t;c;b;a]

To run the query mentioned previously in its functional form, you would write

q)?[t;enlist (>;`time;04:00);(enlist `sym)!enlist `sym;(enlist `minP)!enlist (min;`price)]
sym | minP
----| ----
AAPL| 88
IBM | 30
MSFT| 82

Oh boy…that looks scary!

Let’s dissect it:

t - t (this is the table you are referring to)
c - enlist (>;`time;04:00)

Since we have only one constraint here, we need to enlist (hey! that’s close to the name of this blog) it. If there were more than one constraint, we wouldn’t need enlist. For example:

q)?[t;((=;`sym;enlist`MSFT);(>;`time;04:00));(enlist `sym)!enlist `sym;(enlist `minP)!enlist (min;`price)]
sym | minP
----| ----
MSFT| 82

As you can see, we don’t need that specific enlist but had to add another one to compare sym. You get the point. It will take some time to understand fully where you need enlist.

b - (enlist `sym)!enlist `sym

The b and a arguments both need a dictionary. The key part of the dictionary is the final name you want to assign and value part is the actual column you want to use. In this case, we want to just use `sym as our by clause so we define a dictionary…and since we are just dealing with one by clause, we need to enlist both key and value.

a - (enlist `minP)!enlist (min;`price)

In this case, I want to rename the final column to minP and that’s why I specified that as the key of the dictionary and then the actual formula in the value (min price). Again, since you are talking about just one column, you need to enlist them.

Now, we will get max size as well…notice how enlist is not needed anymore for a clause:

q)?[t;((=;`sym;enlist`MSFT);(>;`time;04:00));(enlist `sym)!enlist `sym;(`minP`maxS)!((min;`price);(max;`size))]
sym | minP maxS
----| ---------
MSFT| 82 130

If you don’t want to have a by clause, you simple say 0b.

q)?[t;((=;`sym;enlist`MSFT);(>;`time;04:00));0b;(enlist `minP)!enlist (min;`price)]
minP
----
82

Exec

Exec statements are the same as select statements with minor differences as shown in examples below.

viagra in the uk There will also be muscle weakness, loss of coordination or even difficulty walking. cheap india viagra Manufactured by Pfizer, this drug is currently patent protected. Today, Urology hospitals in cialis without prescriptions Bangalore have high-precision methods like laparoscopy and endoscopic examination that can be useful in treating major diseases. As a result, this blood buy cialis line check out description storage helps the male organ emerge through outer incitement.
q)?[t;enlist (>;`time;04:00);();()]
time | 14:35:31.860
sym | `IBM
price| 30
size | 105

q)exec sym from t
`MSFT`IBM`IBM`MSFT`AAPL

q)?[t;();();`sym]
`MSFT`IBM`IBM`MSFT`AAPL

q)exec sym,price from t
sym | MSFT IBM IBM MSFT AAPL
price| 73 90 43 90 84

q)?[t;();();(`sym`price)!`sym`price]
sym | MSFT IBM IBM MSFT AAPL
price| 73 90 43 90 84

q)exec price by sym from t
AAPL| ,84
IBM | 90 43
MSFT| 73 90

q)?[t;();`sym;`price]
AAPL| ,84
IBM | 90 43
MSFT| 73 90

Update

Update and Delete use similar syntax as select/exec but instead of ?, they use !

![t;c;b;a]

Let’s say we want to add a column minP which has values for minimum price to our original table t.
In qsql, that would be:

q)update minP:min price by sym from t
time sym price size minP
---------------------------------
04:25:17.604 MSFT 82 130 82
01:47:51.333 IBM 40 167 30
15:50:12.140 AAPL 88 64 77
02:59:16.636 AAPL 77 177 77
14:35:31.860 IBM 30 105 30

For functional form:

t: t
c: nothing
b: sym
a: minP;min price
q)![t;();(enlist`sym)!enlist `sym;(enlist `minP)!enlist (min;`price)]
time sym price size minP
---------------------------------
04:25:17.604 MSFT 82 130 82
01:47:51.333 IBM 40 167 30
15:50:12.140 AAPL 88 64 77
02:59:16.636 AAPL 77 177 77
14:35:31.860 IBM 30 105 30

As you can see, the logic is same as select statement but instead of ?, we use !.

Delete

To delete a column, we use the same syntax as update but the by clause is 0b.
To delete the rows with sym=`MSFT:

q)![t;enlist (=;`sym;enlist`MSFT);0b;`symbol$()]
time sym price size
----------------------------
01:47:51.333 IBM 40 167
15:50:12.140 AAPL 88 64
02:59:16.636 AAPL 77 177
14:35:31.860 IBM 30 105

Parse

If there is a complicated qsql query that you want to rewrite in functional form, you can always use the parse function. For example:

q)parse"select from t where price>75"
?
`t
,,(>;`price;75)
0b
()

This returns the individual values for a functional form (using ? vs ! and values for t,c,b,a). It makes life very easy for more complicated queries.

q)parse"select minP:min price, maxP:max size from t by sym,price where price>75,time within 10:00 12:00"
?
(`t;(`by;(,;`sym;`price)))
,((>;`price;75);(within;`time;10:00 12:00))
0b
`minP`maxP!((min;`price);(max;`size))

As you can see, all you have to do is learn how to properly identify t,c,b and a values and the rest is easy. The sooner you get into the habit of writing functional queries, the better off you will be. In my next post, I will highlight the benefits of functional queries in more detail.

Special thanks to Rolf Neill for feedback and pointing out some mistakes!

Leave a comment

Your email address will not be published. Required fields are marked *