Based on your first example, simplified, but with PK:
CREATE TABLE tbl1 ( tbl1_id serial PRIMARY KEY
Use split_part() to extract the lower and upper split_part() . ( regexp_split_to_array() would be uselessly expensive and error prone). And generate_series() to generate numbers.
Use LATERAL join and aggregate the set immediately to simplify aggregation. the ARRAY constructor is the fastest in this case:
SELECT t.tbl1_id, a.output -- array; added id is optional FROM ( SELECT tbl1_id , split_part(rang, '-', 1)::int AS a , split_part(rang, '-', 2)::int AS z FROM tbl1 ) t , LATERAL ( SELECT ARRAY( -- preserves rows with NULL SELECT g FROM generate_series(a, z, CASE WHEN (za)%2 = 0 THEN 2 ELSE 1 END) g ) AS output ) a;
AIUI, you want each number in the range only if the upper and lower bounds are a mixture of even and odd numbers. Else, only return every second number, which results in even / odd numbers for these cases. This expression implements interval calculation:
CASE WHEN (za)%2 = 0 THEN 2 ELSE 1 END
Result at will:
output ----------------------------- 1,3,5,7,9 2,4,6,8,10 11,12,13,14,15,16,17,18,19,20
In this case, you do not need WITH ORDINALITY because the order of the elements is guaranteed.
The aggregate array_agg() function makes the request a little shorter (but slower) - or use string_agg() to create a string directly, depending on the desired output format:
SELECT a.output -- string FROM ( SELECT split_part(rang, '-', 1)::int AS a , split_part(rang, '-', 2)::int AS z FROM tbl1 ) t , LATERAL ( SELECT string_agg(g::text, ',') AS output FROM generate_series(a, z, CASE WHEN (za)%2 = 0 THEN 2 ELSE 1 END) g ) a;
Note the subtle difference when using the aggregate function or the ARRAY constructor in the LATERAL subquery: usually rows with rang IS NULL are excluded from the result because the LATERAL subquery LATERAL no row .
If you summarize the result right away, "no line" is converted to a single line with a null value, so the original line is saved. I added a demo to the violin.
SQL Fiddle
For this you do not need a CTE, which would be more expensive.
In addition: type conversion to integer automatically removes the gap at the beginning / training, so this line is also suitable for rank : ' 1 - 3' .