SQL operators package: introducing window frames using overSQL function​
​by Damian Calin
The objective is to implement a window frame capability in Mathematica similar with SQL OVER operator supported by most of popular databases.It allows to perform calculations across a set of related rows, without actually grouping these rows together. ​The concept behind SQL window frames are explained more in detail at : ​https://mjk.space/advances-sql-window-frames/​​The new function syntax :​overSQL [Input table , {partition by clause} , {order by clause } , {window frame clause}, {measures to be calculated on window frame spec} ] ​ Its much easier to understand it with a demo.

Load SQL package

Load the last version of SQLOperators package containing SQL function developed so far.
See my previous post : SQL operators as Wolfram Language functions (https://community.wolfram.com/groups/-/m/t/2195893)
In[]:=
CloudImport["https://www.wolframcloud.com/env/damcalrom/Published/SQLOperators.wl"];

Sample data from FinancialData package

In[]:=
(*generatetables*)​​symbols={"MSFT","TSLA","AMZN"};​​sampledata=(Flatten/@Tuples[{{#},FinancialData[#,{{2022}}]//Normal}])&/@symbols//Flatten[#,1]&;​​​​(*convertsdatainSQLtablestructure*)​​sampledataSQL=tableSQL[sampledata,{"symbol","time","close"}];(*input:2dimensionalarrayandalistofcolnames*)​​​​sampledataSQL//​​showTableSQL(*displayfirst10linesbydefault*)
Total Rows:309 Elapsed:3.×
-7
10
Out[]//TableForm=
symbol
time
close
MSFT
Mon 3 Jan 2022 00:00:00GMT+2.
$
334.75
MSFT
Tue 4 Jan 2022 00:00:00GMT+2.
$
329.01
MSFT
Wed 5 Jan 2022 00:00:00GMT+2.
$
316.38
MSFT
Thu 6 Jan 2022 00:00:00GMT+2.
$
313.88
MSFT
Fri 7 Jan 2022 00:00:00GMT+2.
$
314.04
MSFT
Mon 10 Jan 2022 00:00:00GMT+2.
$
314.27
MSFT
Tue 11 Jan 2022 00:00:00GMT+2.
$
314.98
MSFT
Wed 12 Jan 2022 00:00:00GMT+2.
$
318.27
MSFT
Thu 13 Jan 2022 00:00:00GMT+2.
$
304.80
MSFT
Fri 14 Jan 2022 00:00:00GMT+2.
$
310.20

Demo overSQL function

Perform some aggregations on a window frame between current row and 2 preceding row
SQL equivalent will be: ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
In[]:=
sampledataSQL//​​overSQL[#,{symbol}(*partitionby*),​​ {time}(*orderby*),​​ {-2,-1,0}(*windowframerangebetweencurrentrowand2precedingrow*),​​ Association["windowLength"(close//Length),(**)​​ "totalclose"(close//Total),​​ "firstclose"(close//First),​​ "meanclose"(close//Mean)​​ ]]&//​​tableSQLAsDataset
Out[]=
symbol
time
close
windowLength
totalclose
firstclose
meanclose
MSFT
Mon 3 Jan 2022
00:00:00
$
334.75
1
$
334.75
$
334.75
$
334.75
MSFT
Tue 4 Jan 2022
00:00:00
$
329.01
2
$
663.76
$
334.75
$
331.88
MSFT
Wed 5 Jan
2022 00:00:00
$
316.38
3
$
980.14
$
334.75
$
326.71
MSFT
Thu 6 Jan 2022
00:00:00
$
313.88
3
$
959.27
$
329.01
$
319.76
MSFT
Fri 7 Jan 2022
00:00:00
$
314.04
3
$
944.30
$
316.38
$
314.77
MSFT
Mon 10 Jan
2022 00:00:00
$
314.27
3
$
942.19
$
313.88
$
314.06
MSFT
Tue 11 Jan
2022 00:00:00
$
314.98
3
$
943.29
$
314.04
$
314.43
MSFT
Wed 12 Jan
2022 00:00:00
$
318.27
3
$
947.52
$
314.27
$
315.84
MSFT
Thu 13 Jan
2022 00:00:00
$
304.80
3
$
938.05
$
314.98
$
312.68
MSFT
Fri 14 Jan 2022
00:00:00
$
310.20
3
$
933.27
$
318.27
$
311.09
rows 1–10 of 309
For consecutive rows we can use Range function in order to avoid specifying each index
In[]:=
sampledataSQL//​​overSQL[#,{symbol}(*partitionby*),​​ {time}(*orderby*),​​ Range[-2,0](*windowframerangebetweencurrentrowand2precedingrow*),​​ Association["windowLength"(close//Length),(**)​​ "totalclose"(close//Total),​​ "firstclose"(close//First),​​ "meanclose"(close//Mean)​​ ]]&//​​tableSQLAsDataset
Out[]=
symbol
time
close
windowLength
totalclose
firstclose
meanclose
MSFT
Mon 3 Jan 2022
00:00:00
$
334.75
1
$
334.75
$
334.75
$
334.75
MSFT
Tue 4 Jan 2022
00:00:00
$
329.01
2
$
663.76
$
334.75
$
331.88
MSFT
Wed 5 Jan
2022 00:00:00
$
316.38
3
$
980.14
$
334.75
$
326.71
MSFT
Thu 6 Jan 2022
00:00:00
$
313.88
3
$
959.27
$
329.01
$
319.76
MSFT
Fri 7 Jan 2022
00:00:00
$
314.04
3
$
944.30
$
316.38
$
314.77
MSFT
Mon 10 Jan
2022 00:00:00
$
314.27
3
$
942.19
$
313.88
$
314.06
MSFT
Tue 11 Jan
2022 00:00:00
$
314.98
3
$
943.29
$
314.04
$
314.43
MSFT
Wed 12 Jan
2022 00:00:00
$
318.27
3
$
947.52
$
314.27
$
315.84
MSFT
Thu 13 Jan
2022 00:00:00
$
304.80
3
$
938.05
$
314.98
$
312.68
MSFT
Fri 14 Jan 2022
00:00:00
$
310.20
3
$
933.27
$
318.27
$
311.09
rows 1–10 of 309
If we want to specify an unbounded frame we can use Range[-length,0]. This is useful for calculating cumulative totals
SQL equivalent will be: ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.
In[]:=
sampledataSQL//​​overSQL[#,{symbol}(*partitionby*),​​ {time}(*orderby*),​​ Range[-length,0](*windowframerangebetweencurrentrowandunboundedprecedingrow*),​​ Association["windowLength"(close//Length),(**)​​ "totalclose"(close//Total),​​ "firstclose"(close//First),​​ "meanclose"(close//Mean)​​ ]]&//​​tableSQLAsDataset
Out[]=
symbol
time
close
windowLength
totalclose
firstclose
meanclose
MSFT
Mon 3 Jan 2022
00:00:00
$
334.75
1
$
334.75
$
334.75
$
334.75
MSFT
Tue 4 Jan 2022
00:00:00
$
329.01
2
$
663.76
$
334.75
$
331.88
MSFT
Wed 5 Jan
2022 00:00:00
$
316.38
3
$
980.14
$
334.75
$
326.71
MSFT
Thu 6 Jan 2022
00:00:00
$
313.88
4
$
1294.02
$
334.75
$
323.51
MSFT
Fri 7 Jan 2022
00:00:00
$
314.04
5
$
1608.06
$
334.75
$
321.61
MSFT
Mon 10 Jan
2022 00:00:00
$
314.27
6
$
1922.33
$
334.75
$
320.39
MSFT
Tue 11 Jan
2022 00:00:00
$
314.98
7
$
2237.31
$
334.75
$
319.62
MSFT
Wed 12 Jan
2022 00:00:00
$
318.27
8
$
2555.58
$
334.75
$
319.45
MSFT
Thu 13 Jan
2022 00:00:00
$
304.80
9
$
2860.38
$
334.75
$
317.82
MSFT
Fri 14 Jan 2022
00:00:00
$
310.20
10
$
3170.58
$
334.75
$
317.06
rows 1–10 of 309
To reverse the frame use Range[0, length].
SQL equivalent will be: ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING.
The possibility to combine relative position and ranges gives overSQL greater flexibility than SQL equivalent.
Lets says we want a frame containing only the 4th preceding row and a rage between 2 to 5 following row.
This type of condition is not possible with SQL OVER version
If we use an empty range all rows from the partition are used in calculation.
Another nice feature is rowNumber used in combination with over .
Lets say we want first top 3 close positions ordered by value for each symbol