I have a database that has a table called matchstats which includes a column called time and it is updated each time an action takes place. I also have a column called groundstatsid which when it is not null means the action took place on the ground as opposed to standing. Finally I have a column called Round.
Example:
Time | groundstatsid | Round
1 | NULL | 1
8 | NULL | 1
15 | NULL | 1
18 | 1 | 1
20 | 1 | 1
22 | NULL | 1
30 | NULL | 1
1 | NULL | 2
To get the full time standing I would basically want the query to take the first time (1) and store that, then look at groundstatsid until it sees a NON NULL value and take the time at that position, subtract by the earlier number stored to get the time in standup (17). Then it would continue to look for where groundstatsid IS NULL. Once it finds that value it should do the same process of looking until it finds a NON NULL value in groundstatsid or a new round, in which case it will start the whole process again.
Once it has gone through an entire match I would want it to Sum the results.
I would expect the query of the example to return 25.
I would boil this problem down one where you consider pairs of rows, sorted by
timewithin each round. PostgreSQL can do this in one pass — no JOINs, no PL/pgSQL — using window functions:This tells PostgreSQL to read the rows from the table (presumably constrained by
WHERE fightid=?or something), but to consider eachroundseparately for windowing operations. Window functions likefirst_valueandlast_valuecan access the “window”, which I specified to beORDER BY time ROWS 1 PRECEDING, meaning the window contains both the current row and the one immediately preceding it in time (if any). Thus, window functions let us directly output values for both the current row and its predecessor.For the data you provided, this query yields:
Looking at these results helped me decide what to do next. Based on my understanding of your logic, I conclude that the person should be regarded as standing from time 1..1, 1..8, 8..15, 15..18, not standing from 18..20, not standing from 20..22, and is standing again from 22..30. In other words, we want to sum the difference between
first_timeandlast_timewherefirst_is_standingis true. Turning that back into SQL:You could also get other values from this same inner query, like the total time or the number of falls by using
SUM(CASE WHEN ...)to count independent conditions: