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/#!1/a2633/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
;
Here's a demo for this one too: http://sqlfiddle.com/#!1/a2633/2.
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
* The SQL Fiddle demos linked in this answer use the 9.1.8 instance as the 9.2.1 one doesn't appear to be working at the moment.
IN
queries with huge sets are notoriously slow. It's often faster to use a JOIN
instead:
SELECT nodes
FROM planet_osm_ways
JOIN (
SELECT ltrim(member, 'w')::bigint AS id
FROM (
SELECT unnest(members) AS member
FROM planet_osm_rels
WHERE (tags_hstore @> '"type"=>"boundary", "admin_level"=>"2", ...')
) u
WHERE member LIKE 'w%'
) x USING (id);
But that's not the most important problem here. I wonder why the GIN index planet_osm_rels_tags_hstore_idx
is not being used. Are you selecting large enough parts of the table planet_osm_rels
to justify a sequential scan?
Oh, and id
is type bigint
. So cast to bigint
instead of int
for less friction.
If you can extract "way IDs" and save them redundantly in a separate column way_ids bigint[]
in your table, your query would become quite a bit simpler and faster, with one less subquery level:
SELECT nodes
FROM planet_osm_ways
JOIN (
SELECT unnest(way_ids) AS id
FROM planet_osm_rels
WHERE (tags_hstore @> '"type"=>"boundary", "admin_level"=>"2", ...')
) u USING (id);
Best Answer
That looks like an array. If that is the case, you can remove the first element of the array using an UPDATE statement:
[2:]
selects all elements of the array starting with the second. The result of that is then used to override the existing array.