Requirement is to search at a table of numbers that have different states each, like "Available", "Reserved", "Disconnected", etc., to find the "Available" only numbers that are consecutive and at least 50 consecutive every time, and return those. Example data:
Number State
124 "Reserved"
125 "Available"
126 "Available"
127 "Disconnected"
128 "Available"
129 "Available"
130 "Available"
131 "Available"
132 "Available"
133 "Reserved"
.
.
.
So, at above case, at least the 128 - 132 should get returned since they are 5 "Available" numbers. Then next consecutive "Available" could be 7 or 10 or 15, those should also get returned, as soon as they are 5 or more. Hope request is clear. Thank you.
If you are using Oracle 12.1 or higher, the match_recognize
solution in Vamsi Prabhala's Answer is probably the most efficient.
For older versions, a solution using the fixed differences (aka Tabibitosan) method is probably best. I show the count for each sequence (although strictly speaking you may not need that).
with
sample_data as (
select 124 as num, 'Reserved' as state from dual union all
select 125 , 'Available' from dual union all
select 126 , 'Available' from dual union all
select 127 , 'Disconnected' from dual union all
select 128 , 'Available' from dual union all
select 129 , 'Available' from dual union all
select 130 , 'Available' from dual union all
select 131 , 'Available' from dual union all
select 132 , 'Available' from dual union all
select 133 , 'Reserved' from dual union all
select 135 , 'Troubled' from dual union all
select 136 , 'Taken' from dual union all
select 137 , 'Available' from dual union all
select 138 , 'Available' from dual union all
select 139 , 'Available' from dual union all
select 140 , 'Available' from dual union all
select 141 , 'Available' from dual union all
select 142 , 'Available' from dual
)
select num, ct
from (
select num, count(*) over (partition by grp) ct
from (
select num, num - row_number() over (order by num) as grp
from sample_data
where state = 'Available'
)
)
where ct >= 5
order by num
;
Output:
NUM CT
---------- ----------
128 5
129 5
130 5
131 5
132 5
137 6
138 6
139 6
140 6
141 6
142 6
One method uses a bunch of lags:
select t.*
from (select t.*,
lag(state, 1) over (order by number) as state_1,
lag(state, 2) over (order by number) as state_2,
lag(state, 3) over (order by number) as state_3,
lag(state, 4) over (order by number) as state_4
from t
) t
where state = 'Available' and state_1 = state and state_2 = state and state_3 = state and state_4 = state;
This returns only the 5 (and subsequent rows), but it is easy enough to figure out the earlier ones.
With that in mind, there is a simpler method:
select t.*
from (select t.*,
lag(number, 4) over (partition by state order by number) as number_state_4
from t
) t
where state = 'Available' and
number_state_4 = number - 4;
This is saying that the 4th row back for 'Available'
is the current row minus 4 -- or that there are 5 "Available"s in a row.
Of course, you can also approach this as gaps and islands:
select state, min(number), max(number)
from (select t.*,
row_number over (partition by state order by number) as seqnum
from t
) t
where state = 'Available'
group by state, number - seqnum
having count(*) >= 5
If you are using Oracle version 12c, there is an option to use match_recognize
, which does a pattern matching to get rows with a specified pattern.
select *
from tbl
MATCH_RECOGNIZE (
ORDER BY "number"
ALL ROWS PER MATCH
AFTER MATCH SKIP TO LAST AVAILABLE
PATTERN(available{5,})
DEFINE
available AS (status='Available')
) MR
ORDER BY "number"
You can do this easily via PLsql block if this can help you:
DECLARE
CuRSOR L1 is
SELECT * from tab1;
L_COUNT number:= 0;
l_number_seats number := &1;
l_start_seat number;
l_end_seat number;
BEGIN
l_start_seat := 0;
l_end_seat := 0;
for i in l1
loop
if L_COUNT = l_number_seats THEN
EXIT;
end if;
if i.state = 'Available' then
if L_COUNT =0 then
l_start_seat := i;
else
l_end_seat := i;
end if;
L_COUNT := L_COUNT +1;
else
L_COUNT := 0;
end if;
END loop;
dbms_output.put_line(l_start_seat||' - '||l_end_seat);
END;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.