patternsqlMinor
PostgreSQL: result of range difference would not be contiguous
Viewed 0 times
postgresqlresultrangedifferencewouldcontiguousnot
Problem
How to substract a smaller subrange from a bigger range?
Result:
SQL:
The result does not fit into a int4range since it is not contiguous.
How to get the result as a set of int4ranges?
123456789
567Result:
1234 89SQL:
select '[1,9]'::int4range - '[5,7]'::int4range;
ERROR: result of range difference would not be contiguousThe result does not fit into a int4range since it is not contiguous.
How to get the result as a set of int4ranges?
Solution
You could define your own functions range_add/range_sub (intersection doesn't need special handling):
You could define them like this:
Unfortunately, PostgreSQL is missing native support for this set-based behavior.
select range_add('[1,5)'::int4range, '[8,10)'::int4range);
range_add
--------------------
{"[1,5)","[8,10)"}
select range_sub('[1,10)'::int4range, '[5,8)'::int4range);
range_sub
--------------------
{"[1,5)","[8,10)"}You could define them like this:
create or replace function range_add(int4range, int4range) returns int4range[] as
$
select
case
when $1 && $2
then array[$1 + $2]
else array[$1, $2]
end
$
language SQL;
create or replace function range_sub(int4range, int4range) returns int4range[] as
$
select
case
when $1 @> $2 and not isempty($2) and lower($1) <> lower($2) and upper($2) <> upper($1)
then array[int4range(lower($1), lower($2), '[)'), int4range(upper($2), upper($1), '[)')]
else array[$1 - $2]
end
$
language SQL;Unfortunately, PostgreSQL is missing native support for this set-based behavior.
Code Snippets
select range_add('[1,5)'::int4range, '[8,10)'::int4range);
range_add
--------------------
{"[1,5)","[8,10)"}
select range_sub('[1,10)'::int4range, '[5,8)'::int4range);
range_sub
--------------------
{"[1,5)","[8,10)"}create or replace function range_add(int4range, int4range) returns int4range[] as
$$
select
case
when $1 && $2
then array[$1 + $2]
else array[$1, $2]
end
$$
language SQL;
create or replace function range_sub(int4range, int4range) returns int4range[] as
$$
select
case
when $1 @> $2 and not isempty($2) and lower($1) <> lower($2) and upper($2) <> upper($1)
then array[int4range(lower($1), lower($2), '[)'), int4range(upper($2), upper($1), '[)')]
else array[$1 - $2]
end
$$
language SQL;Context
StackExchange Database Administrators Q#97779, answer score: 4
Revisions (0)
No revisions yet.