Range Types
A range type represents a span of values of some underlying type — all the integers between two numbers, all the timestamps between two points in time, all the dates in a booking. Instead of modeling a range as two separate columns (start_date, end_date) and writing overlap logic by hand every time you query it, PostgreSQL lets you store and query the range as a single, well-defined value.
Type | Range of |
|---|---|
| Integers |
| Bigints |
| Numeric (exact decimal) values |
| Timestamps without timezone |
| Timestamps with timezone |
| Dates |
Bounds: inclusive and exclusive
A range literal is written as a lower bound, a comma, and an upper bound, wrapped in brackets that control whether each end is inclusive or exclusive: [ and ] are inclusive, ( and ) are exclusive.
Range literals
SELECT '[3, 7]'::int4range; -- 3 through 7, both included SELECT '[3, 7)'::int4range; -- 3 through 6 — 7 excluded SELECT '[2026-08-01, 2026-08-05)'::daterange; -- a 4-night stay, checkout day excluded
A practical example: reservations
Date ranges are a natural fit for modeling a booking or reservation's duration.
Storing a reservation as a range
CREATE TABLE reservations (
reservation_id SERIAL PRIMARY KEY,
room_id INTEGER NOT NULL,
stay DATERANGE NOT NULL
);
INSERT INTO reservations (room_id, stay) VALUES
(101, '[2026-08-01, 2026-08-05)'),
(101, '[2026-08-10, 2026-08-12)');Range operators
Operator | Meaning |
|---|---|
| Contains — the range contains this element or sub-range |
| Overlaps — the two ranges share at least one point |
| Strictly left of |
| Strictly right of |
Checking for overlapping bookings
-- Does a specific date fall within any existing reservation for room 101? SELECT * FROM reservations WHERE room_id = 101 AND stay @> DATE '2026-08-03'; -- Would a new booking from Aug 4 to Aug 8 overlap any existing reservation? SELECT * FROM reservations WHERE room_id = 101 AND stay && '[2026-08-04, 2026-08-08)'::daterange;
reservation_id | room_id | stay
---------------+---------+------------------------
1 | 101 | [2026-08-01,2026-08-05)That second query is exactly the check a booking system needs to run before confirming a new reservation — "does this proposed date range overlap anything already on the books for this room?" — expressed as a single, efficient condition instead of a hand-rolled start_date < ? AND end_date > ? comparison.
Range types (
INT4RANGE,NUMRANGE,TSRANGE,DATERANGE, and more) store a span of values as a single value.Bounds use
[/]for inclusive and(/)for exclusive.@>tests whether a range contains a value or sub-range;&&tests whether two ranges overlap.A date range column is a natural, queryable way to model a reservation or booking window.
Combined with an exclusion constraint, range types let PostgreSQL enforce "no overlapping reservations" directly at the database level.