postmodern-w (when, window, with, with-recursive)
[Special Characters][A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z]
When
(let ((param-latitude nil) (param-longitude t))
(query (:select 'id 'name
(when param-latitude '0)
(when param-longitude 'longitude)
:from 'countries
:where (:= 'id 20))))
((20 "UK" NIL -2))
Window
As stated in the postgresql documentation: "When a query involves multiple window functions, it is possible to write out each one with a separate OVER clause, but this is duplicative and error-prone if the same windowing behavior is wanted for several functions. Instead, each windowing behavior can be named in a WINDOW clause and then referenced in OVER". http://www.postgresql.org/docs/9.1/static/tutorial-window.html. They are available in postmodern as of the 29 October 2013 additions to the git repository.
(query (:select (:over (:sum 'salary) 'w)
(:over (:avg 'salary) 'w)
:from 'empsalary :window
(:as 'w (:partition-by 'depname :order-by (:desc 'salary)))))
With
With queries are often referred to as Common Table Expressions. They are used as ways to write auxiliary statements for use in a larger query. The Postgresql documentation covers them at http://www.postgresql.org/docs/9.1/static/queries-with.html.
(query
(:with
(:as 'upd
(:parens
(:update 'employees
:set 'sales-count (:= 'sales-count 1)
:where (:= 'id
(:select 'sales-person
:from 'accounts
:where (:= 'name "Acme Corporation")))
:returning '*)))
(:insert-into 'employees-log
(:select '* 'current-timestamp
:from
'upd))))
With-recursive
With-recursive allows the with auxiliary statement to refer to itself. These queries match the following template:
WITH RECURSIVE [temp table] [column list]
AS ( [seed statement]
UNION ALL [recursive statement - effectively looping through the table] )
[outer query which specifies the fields to be kept in the final result and throws away the intermediate results]
A few postmodern samples follow which match up with the postgresql documentation examples:
(query
(:with-recursive
(:as (:t1 'n)
(:union-all (:values (:set 1))
(:select (:+ 'n 1)
:from 't1
:where (:< 'n 100))))
(:select (:sum 'n) :from 't1))
:single)
(query
(:with-recursive
(:as (:included-parts 'sub-part 'part 'quantity)
(:union-all
(:select 'sub-part 'part 'quantity
:from 'parts
:where (:= 'part "our-product"))
(:select 'p.sub-part 'p.part 'p.quantity
:from (:as 'included-parts 'pr)
(:as 'parts 'p)
:where (:= 'p.part 'pr.sub-part))))
(:select 'sub-part (:as (:sum 'quantity) 'total-quantity)
:from 'included-parts
:group-by 'sub-part)))
(query
(:with-recursive
(:as (:search-graph 'id 'link 'data 'depth)
(:union-all
(:select 'g.id 'g.link 'g.data 1
:from (:as 'graph 'g))
(:select 'g.id 'g.link 'g.data (:= 'sg.depth 1)
:from (:as 'graph 'g) (:as 'search-graph 'sg)
:where (:= 'g.id 'sg.link))))
(:select '* :from 'search-graph)))
(query
(:with-recursive
(:as (:search-graph 'id 'link 'data'depth 'path 'cycle)
(:union-all
(:select 'g.id 'g.link 'g.data 1
(:[] 'g.f1 'g.f2) nil
:from (:as 'graph 'g))
(:select 'g.id 'g.link 'g.data (:= 'sg.depth 1)
(:|| 'path (:row 'g.f1 'g.f2))
(:= (:row 'g.f1 'g.f2)
(:any* 'path))
:from (:as 'graph 'g)
(:as 'search-graph 'sg)
:where (:and (:= 'g.id 'sg.link)
(:not 'cycle)))))
(:select '* :from 'search-graph)))
As a different example, consider a quicklisp dependency table where the fields are 'depends_on' and 'depended_on'. In other words library staple depends-on alexandria. So one record has "staple" in the depends_on column and "alexandria" in the depended_on column.
A function to return a list of all the dependencies of a quicklisp library (assuming the data is in a table called "dependencies") could look like this:
(defun list-dependencies (lib-name)
"Returns a list of the names of the direct and indirect libraries depended-on by lib-name."
(sort (alexandria:flatten
(postmodern:query
(:with-recursive
(:as 'children
(:union
(:select 'depended-on
:from 'dependencies
:where (:= 'depends-on '$1))
(:select 'a.depended-on
:from (:as 'dependencies 'a)
:inner-join (:as 'children 'b)
:on (:= 'a.depends-on 'b.depended-on))))
(:select '* :from 'children))
lib-name))
#'string<))