POSTGRESQL RANGE TYPES
A range type in PostgreSQL represents a continuous interval of values as a single data type.
Instead of storing two separate columns like `start_date` and `end_date`, PostgreSQL allows you to store the interval in one logical field such as `period daterange`. A range is a first-class datatype, fully supported by operators, indexes, and constraints.
Built-in Range Types
PostgreSQL provides the following native range types:
|
Range Type |
Base Type |
Description |
|
int4range |
integer |
32-bit integer range |
|
int8range |
bigint |
64-bit integer range |
|
numrange |
numeric |
arbitrary precision numeric range |
|
daterange |
date |
calendar date range |
|
tsrange |
timestamp |
timestamp without timezone |
|
tstzrange |
timestamptz |
timestamp with timezone |
Basic Range Syntax
A range is defined using brackets:
[ lower , upper ]
|
Syntax |
Meaning |
|
[1,10] |
include both 1 and 10 |
|
(1,10) |
exclude both 1 and 10 |
|
[1,10) |
include 1, exclude 10 |
|
(1,10] |
exclude 1, include 10 |
Empty Ranges
An empty range represents a valid range value containing no elements.
SELECT 'empty'::int4range;
Unbounded Ranges
Ranges can be open-ended on either side.
SELECT '[2025-01-01,)'::daterange;
SELECT '(,2025-01-01]'::daterange;
Range Constructors
Ranges can also be built using constructor functions instead of literals.
SELECT daterange('2025-01-01', '2025-12-31', '[)');
General syntax:
range_type(lower, upper, bounds)
Core Logical Operators
PostgreSQL provides rich built-in operators for range processing:
|
Operator |
Meaning |
|
&& |
overlaps |
|
@> |
contains |
|
<@ |
is contained by |
|
- |
- |
|
<< |
strictly left of |
|
>> |
strictly right of |
|
&< |
does not extend to right |
|
&> |
does not extend to left |
Core Functions
A set of built-in functions allows extracting and analyzing range properties.
|
Function |
Meaning |
|
lower(range) |
returns lower bound |
|
upper(range) |
returns upper bound |
|
isempty(range) |
checks if range is empty |
|
lower_inc(range) |
is lower bound inclusive |
|
upper_inc(range) |
is upper bound inclusive |
|
range_merge(a,b) |
union of two ranges |
Short description of gist and sp-gist indexes, we will review then separately.
Indexing Range Types – GiST
For most range-based operations PostgreSQL uses GiST indexes, which are designed for non-standard comparisons such as overlap and containment.
CREATE INDEX ON table USING gist (range_column);
This index supports:
Alternative Index Type – SP-GiST
SP-GiST is another index type that can sometimes be more compact or efficient depending on data distribution.
CREATE INDEX ON table USING spgist (range_column);
Choice between GiST and SP-GiST depends on workload characteristics.
Real Business Scenarios
Range types are ideal for systems dealing with:
Any domain involving time intervals or numeric intervals benefits directly from range types.
PostgreSQL vs Other RDBMS – Feature Comparison
|
Feature |
PostgreSQL |
Oracle |
SQL Server |
MySQL |
|
Native range datatype |
YES |
NO |
NO |
NO |
|
Range operators |
YES |
NO |
NO |
NO |
|
Overlap operator (&&) |
YES |
NO |
NO |
NO |
|
Containment operator (@>) |
YES |
NO |
NO |
NO |
|
Range indexes |
YES |
NO |
NO |
NO |
|
Exclusion constraints |
YES |
NO |
NO |
NO |
|
Built-in range functions |
YES |
NO |
NO |
NO |
Functional Equivalents in Other Databases
To simulate PostgreSQL range behavior in Oracle, SQL Server, or MySQL, developers must store two separate columns.
Instead of:
period daterange
Other databases must use:
start_date DATE,
end_date DATE
And manually emulate overlap logic:
WHERE new.start <= existing.end
AND new.end >= existing.start
This approach is:
PostgreSQL range types provide:
Other RDBMS platforms can only imitate this behavior using manual patterns such as triggers, stored procedures, or complex application logic. PostgreSQL delivers the functionality natively at the database engine level.