fixes #551 by supporting multiple window clauses

Signed-off-by: Sean Corfield <sean@corfield.org>
This commit is contained in:
Sean Corfield 2024-11-18 15:02:28 -08:00
parent 6fa606ffd5
commit 21ce3a2242
No known key found for this signature in database
4 changed files with 64 additions and 2 deletions

View file

@ -1,6 +1,7 @@
# Changes
* 2.6.next in progress
* Fix [#551](https://github.com/seancorfield/honeysql/issues/551) by supporting multiple `WINDOW` clauses.
* Fix [#549](https://github.com/seancorfield/honeysql/issues/549) by using `:bb` conditionals to support Babashka (and still support Clojure 1.9.0), and add testing against Babashka so it is fully-supported as a target via PR [#550](https://github.com/seancorfield/honeysql/pull/550) [@borkdude](https://github.com/borkdude)
* Address [#532](https://github.com/seancorfield/honeysql/issues/532) by adding support for `EXCLUDE` and `RENAME` and starting to write tests for XTDB compatibility.

View file

@ -1090,7 +1090,7 @@ but is rendered into the SQL later in precedence order.
## window, partition-by (and over)
`:window` accepts a pair of SQL entity (the window name)
`:window` accept alternating pairs of SQL entity (the window name)
and the window "function" as a SQL clause (a hash map).
`:partition-by` accepts the same arguments as `:select` above
@ -1116,6 +1116,25 @@ SELECT id, AVG(salary) OVER (PARTITION BY department ORDER BY designation ASC) A
FROM employee
WINDOW w AS (PARTITION BY department)
"]
;; multiple windows:
user=> (sql/format {:select [:id
[[:over
[[:avg :salary]
{:partition-by [:department]
:order-by [:designation]}
:Average]
[[:max :salary]
:w
:MaxSalary]]]]
:from [:employee]
:window [:w {:partition-by [:department]}
:x {:partition-by [:salary]}]}
{:pretty true})
["
SELECT id, AVG(salary) OVER (PARTITION BY department ORDER BY designation ASC) AS Average, MAX(salary) OVER w AS MaxSalary
FROM employee
WINDOW w AS (PARTITION BY department), x AS (PARTITION BY salary)
"]
;; easier to write with helpers (and easier to read!):
user=> (sql/format (-> (select :id
(over [[:avg :salary] (-> (partition-by :department) (order-by :designation)) :Average]
@ -1128,6 +1147,18 @@ SELECT id, AVG(salary) OVER (PARTITION BY department ORDER BY designation ASC) A
FROM employee
WINDOW w AS (PARTITION BY department)
"]
;; multiple window clauses:
user=> (sql/format (-> (select :id
(over [[:avg :salary] (-> (partition-by :department) (order-by :designation)) :Average]
[[:max :salary] :w :MaxSalary]))
(from :employee)
(window :w (partition-by :department))
(window :x (partition-by :salary))) {:pretty true})
["
SELECT id, AVG(salary) OVER (PARTITION BY department ORDER BY designation ASC) AS Average, MAX(salary) OVER w AS MaxSalary
FROM employee
WINDOW w AS (PARTITION BY department), x AS (PARTITION BY salary)
"]
```
The window function in the `:over` expression may be `{}` or `nil`:

View file

@ -953,6 +953,9 @@
(defn- format-selector [k xs]
(format-selects k [xs]))
(defn- format-window [k xs]
(format-selects k (into [] (partition-all 2 xs))))
(declare columns-from-values)
(defn- format-insert [k table]
@ -1643,7 +1646,7 @@
:where #'format-on-expr
:group-by #'format-group-by
:having #'format-on-expr
:window #'format-selector
:window #'format-window
:partition-by #'format-selects
:order-by #'format-order-by
:limit #'format-on-expr

View file

@ -533,6 +533,33 @@
" MAX(salary) OVER w AS MaxSalary"
" FROM employee"
" WINDOW w AS (PARTITION BY department)")]))
;; multiple window tests
(is (= (-> (select :id
(over [[:avg :salary] (-> (partition-by :department) (order-by :designation)) :Average]
[[:max :salary] :w :MaxSalary]))
(from :employee)
(window :w (partition-by :department))
(window :x (partition-by :salary))
sql/format)
[(str "SELECT id,"
" AVG(salary) OVER (PARTITION BY department ORDER BY designation ASC) AS Average,"
" MAX(salary) OVER w AS MaxSalary"
" FROM employee"
" WINDOW w AS (PARTITION BY department)"
", x AS (PARTITION BY salary)")]))
(is (= (-> (select :id
(over [[:avg :salary] (-> (partition-by :department) (order-by :designation)) :Average]
[[:max :salary] :w :MaxSalary]))
(from :employee)
(window :w (partition-by :department)
:x (partition-by :salary))
sql/format)
[(str "SELECT id,"
" AVG(salary) OVER (PARTITION BY department ORDER BY designation ASC) AS Average,"
" MAX(salary) OVER w AS MaxSalary"
" FROM employee"
" WINDOW w AS (PARTITION BY department)"
", x AS (PARTITION BY salary)")]))
;; test nil / empty window function clause:
(is (= (-> (select :id
(over [[:avg :salary] {} :Average]