sql-server

7 Post

oracle

7 Post

postgresql

10 Post

my-sql

2 Post

common-sql

1 Post

News

5 News

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.

PostgreSQL range types provide:

  • correctness by design
  • declarative interval logic
  • built-in operators
  • proper index support
  • seamless constraint integration
  • safer concurrency handling

 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.

Let’s take a practical overview and see how it implemented.

Built-in 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)

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:

  • overlap checks
  • containment checks
  • adjacency checks
  • exclusion constraints

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:

  • employment periods
  • insurance coverage intervals
  • product pricing validity
  • hotel or resource reservations
  • promotions and campaigns
  • tax rate validity
  • interest rate schedules

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:

  • error-prone
  • harder to maintain
  • not index-friendly
  • not constraint-safe
  • more complex to optimize