Reputation: 447
I am looking to figure out the correct way to build this statement in SQL (oracle). What I have is a columns in a table that consists of "Date" and "Direction". In the direction column it either is a "North" or "South". I am trying to figure out how to output the # of times in a row before the row occurred (like a Win or Loss Streak) did "North" or "South appear". For example:
SELECT date, direction, ? As Streak FROM temptable order by date ASC;
Sample dataset of 8 rows:
The results would be:
date, Direction, Streak
Upvotes: 0
Views: 116
Reputation: 9825
You can do this with SQL Pattern Matching:
with rws as (
select date'2022-01-01' dt, 'North' dir from dual union all
select date'2022-01-02' dt, 'North' dir from dual union all
select date'2022-01-03' dt, 'North' dir from dual union all
select date'2022-01-04' dt, 'North' dir from dual union all
select date'2022-01-05' dt, 'South' dir from dual union all
select date'2022-01-06' dt, 'South' dir from dual union all
select date'2022-01-07' dt, 'South' dir from dual union all
select date'2022-01-08' dt, 'North' dir from dual
)
select * from rws match_recognize (
order by dt
measures
count(*) - 1 as streak
all rows per match
pattern ( init same* )
define
same as dir = init.dir
);
DT STREAK DIR
----------- ---------- -----
01-JAN-2022 0 North
02-JAN-2022 1 North
03-JAN-2022 2 North
04-JAN-2022 3 North
05-JAN-2022 0 South
06-JAN-2022 1 South
07-JAN-2022 2 South
08-JAN-2022 0 North
The pattern
is a regular expression using the variables listed in the define
clause.
So it's searching for:
init
) - this is undefined so "always true"same
variablesdir
matches that of the init
"always true" rowThe measures
clause defines output columns. count(*)
returns the running total of rows matched in the group so far.
The all rows per match
clause is necessary because by default match_recognize
returns one row per group. So you'd only get one row in the output for each streak.
Upvotes: 1
Reputation: 32609
Not specifically Oracle but this is a standard approach using row_number
to identify sequential groups, then to count within those groups:
with g as (
select *,
Row_Number() over(order by date)
- Row_Number() over(partition by direction order by date) gn
from t
)
select *,
Row_Number() over(partition by gn order by date) - 1 as Streak
from g
order by date;
Upvotes: 2