This is a gaps-and-islands problem. Assuming there are no gaps or duplicates in the same id_set
set:
WITH partitioned AS (
SELECT
*,
number - ROW_NUMBER() OVER (PARTITION BY id_set) AS grp
FROM atable
WHERE status = 'FREE'
),
counted AS (
SELECT
*,
COUNT(*) OVER (PARTITION BY id_set, grp) AS cnt
FROM partitioned
)
SELECT
id_set,
number
FROM counted
WHERE cnt >= 3
;
Here's a SQL Fiddle demo link for this query: http://sqlfiddle.com/#!12/59db6/1.
UPDATE
To return only one set, you could add in one more round of ranking:
WITH partitioned AS (
SELECT
*,
number - ROW_NUMBER() OVER (PARTITION BY id_set) AS grp
FROM atable
WHERE status = 'FREE'
),
counted AS (
SELECT
*,
COUNT(*) OVER (PARTITION BY id_set, grp) AS cnt
FROM partitioned
),
ranked AS (
SELECT
*,
RANK() OVER (ORDER BY id_set, grp) AS rnk
FROM counted
WHERE cnt >= 3
)
SELECT
id_set,
number
FROM ranked
WHERE rnk = 1
;
If you ever need to make it one set per id_set
, change the RANK()
call like this:
RANK() OVER (PARTITION BY id_set ORDER BY grp) AS rnk
Additionally, you could make the query return the smallest matching set (i.e. first try to return the first set of exactly three consecutive numbers if it exists, otherwise four, five etc.), like this:
RANK() OVER (ORDER BY cnt, id_set, grp) AS rnk
or like this (one per id_set
):
RANK() OVER (PARTITION BY id_set ORDER BY cnt, grp) AS rnk