-
Notifications
You must be signed in to change notification settings - Fork 0
/
9.window-func.sql
177 lines (148 loc) · 4.93 KB
/
9.window-func.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/**
Window functions Provide aggregetes based upon the current row.
For ranking, running totals(промежуточные итоги),
rolling averages(скользящие средние).
Allows to have several different groups
or partitions for single query.
Saddly, but sqliteman does not support window functions.
Probably the new version https://github.com/rparkins999/sqliteman supports it,
but there is no build for windows.
*/
-- simple average
SELECT COUNT(Handicap) AS Count, AVG(Handicap * 1.0) as Average
FROM Member;
-- what if we want to have other data from member ?
-- query below wont work as expected
select
member.*, COUNT(Handicap) AS Count,
AVG(Handicap * 1.0) as Average
from member
;
-- this might be useful
select * from member
join (
select
COUNT(Handicap) AS Count,
AVG(Handicap * 1.0) as Average
from member
);
-- but there is a special solution
-- window function allows combine aggregates with data.
select memberid, firstname, lastname, handicap,
count(handicap) OVER() as Count,
ROUND(avg(handicap) OVER(), 2) as AverageHandicap
from member
;
-- what it brings to us ?
-- well, now we can calc the diff between avg nahdicap
-- and member handicap
select memberid, firstname, lastname, handicap,
ROUND(avg(handicap) OVER(), 2) as AverageHandicap,
ROUND(handicap-avg(handicap) OVER(), 2) as Difference
from member
;
-- can you do it without window function?
select memberid, firstname, lastname, handicap,
ROUND(avgh.h, 2) as AverageHandicap,
ROUND(handicap-avgh.h, 2) as Difference
from member
join (select avg(handicap) h from member) AvgH
;
-- Easily. So what's the point for window ?
/** PARTITIONS */
-- With partitions we can have several grouping inside one query,
-- while with GROUP BY we can have ONLY ONE group
-- Get info from Entry table. Show total entries,
-- count of entries by tournament, count of entries
-- by tournament and year in one table
SELECT MemberID, TourID, Year,
COUNT(*) OVER() as CountAll,
COUNT(*) OVER(PARTITION BY TourID) AS CountTour,
COUNT(*) OVER(PARTITION BY TourID, Year) AS CountTourYear
FROM Entry;
-- Let's ask something by myself
-- Show for each member count of all entries
-- along with count of entries for each tournament
-- that member entered
select memberid, TourID, year,
count(*) OVER(PARTITION BY memberid) as TotalEntries,
count(*) OVER(PARTITION BY MemberID, TourID) as TourEntries
from Entry
;
/** ORDER BY */
-- ORDER BY allows control order of calculating. It can be used inside OVER().
-- Cumulative effect. Run this query to see what it means.
-- Notice the Year and Last Cumulative columns.
SELECT MemberID, TourID, Year,
COUNT(*) OVER(ORDER BY Year /* DESC */ ) AS Cumulative
FROM Entry;
-- NOTE if we add DESC to ORDER BY inside OVER()
-- we will get other results
/** RANKINKG */
-- RANK() set rating based on current row position.
-- If value of Handicap is changing then new Rank value is using.
SELECT MemberID, Handicap,
RANK() OVER (ORDER BY Handicap) AS Rank
FROM Member
-- excluding null, otherwise NULL handicap also will be ranked
WHERE Handicap IS NOT NULL
;
-- Running total on Handicap (dont know for what)
SELECT MemberID, Handicap,
TOTAL(Handicap) OVER (ORDER BY Handicap) AS Total
FROM Member
;
/** Combining Ordering with Partitions */
-- While this is possible, there is no good example in the book =\
-- Provided query is related to table which is not exist in db.
-- So basic idea is:
-- Make partition first then order within each partition
-- this query shows
-- How many concrete tournament there was on each year.
-- Note cumulative effect of TourCount
SELECT MemberID, TourID, Year,
COUNT(*) OVER(PARTITION BY TourID ORDER BY Year) AS "TourCount",
COUNT(*) OVER(PARTITION BY TourID, Year) AS CountTourYear
FROM Entry;
/** Framing */
-- Allows to set which rows to use in a "window".
-- With them we now can see that this is really a WINDOW to process data.
-- Sample query to get averages and 3-month averages
-- NOTE the table is not in the database
SELECT Month, Area, Income,
-- This is a default FRAME.
-- Each next row will calculate considering all the prevent rows
AVG(Income) OVER(
PARTITION BY AREA
ORDER BY Month
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS AreaRunningAverage,
-- This is a custom FRAME.
-- Each avg calculation is based on 1 previous and 1 next row.
AVG(Income) OVER(
PARTITION BY AREA
ORDER BY Month
ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING
) AS Area3MonthAverage
FROM Income;
/** SUMMARY */
/**
Window function allows to get both aggregate and detail info
available in the same query.
*/
/* New practice after some time */
-- trying to print row number
select
memberid
, row_number() over w as row_num
, lastname
, firstname
from Member
window w as (order by memberid);
-- the same result
select
memberid
, row_number() over(order by memberid) as row_num
, lastname
, firstname
from Member;