- Range types are data types representing a range of values over some
- sub-type with a total order. For instance, ranges
+ Range types are data types representing a range of values of some
+ element type (called the range's subtype>).
+ For instance, ranges
of timestamp might be used to represent the ranges of
time that a meeting room is reserved. In this case the data type
- is tsrange (short for "timestamp range"),
- and timestamp is the sub-type with a total order.
+ is tsrange (short for timestamp range
),
+ and timestamp is the subtype. The subtype must have
+ a total order so that it is well-defined whether element values are
+ within, before, or after a range of values.
- Range types are useful because they represent many points in a
- single value. The use of time and date ranges for scheduling
+ Range types are useful because they represent many element values in a
+ single range value, and because concepts such as overlapping ranges can
+ be expressed clearly. The use of time and date ranges for scheduling
purposes is the clearest example; but price ranges, measurement
- ranges from an instrument, etc., are also useful.
+ ranges from an instrument, and so forth can also be useful.
Built-in Range Types
+
PostgreSQL comes with the following built-in range types:
- INT4RANGE -- Range of INTEGER. This is a discrete range type, see .
+ INT4RANGE — Range of INTEGER
- INT8RANGE -- Range of BIGINT. This is a discrete range type, see .
+ INT8RANGE — Range of BIGINT
- NUMRANGE -- Range of NUMERIC.
+ NUMRANGE — Range of NUMERIC
- TSRANGE -- Range of TIMESTAMP WITHOUT TIME ZONE.
+ TSRANGE — Range of TIMESTAMP WITHOUT TIME ZONE
- TSTZRANGE -- Range of TIMESTAMP WITH TIME ZONE.
+ TSTZRANGE — Range of TIMESTAMP WITH TIME ZONE
- DATERANGE -- Range of DATE. This is a discrete range type, see .
+ DATERANGE — Range of DATE
- In addition, you can define your own; see for more information.
+ In addition, you can define your own range types;
+ see for more information.
Examples
+
-CREATE TABLE reservation ( during TSRANGE );
-INSERT INTO reservation VALUES
- ( '[2010-01-01 14:30, 2010-01-01 15:30)' );
+CREATE TABLE reservation ( room int, during TSRANGE );
+INSERT INTO reservation VALUES
+ ( 1108, '[2010-01-01 14:30, 2010-01-01 15:30)' );
-- Containment
SELECT int4range(10, 20) @> 3;
-- Overlaps
-SELECT numrange(11.1, 22.2) && numrange(20.0, 30.0);
+SELECT numrange(11.1, 22.2) && numrange(20.0, 30.0);
--- Find the upper bound:
+-- Extract the upper bound
SELECT upper(int8range(15, 25));
--- Compute the intersection:
+-- Compute the intersection
SELECT int4range(10, 20) * int4range(15, 25);
-- Is the range non-empty?
SELECT isempty(numrange(1, 5));
-
- See
- and for complete lists of
- functions and operators on range types.
+ See
+ and for complete lists of
+ functions and operators on range types.
Inclusive and Exclusive Bounds
+
- Every range has two bounds, the lower bound and the upper bound. All
- points in between those values are included in the range. An
- inclusive bound means that the boundary point itself is included in
- the range as well, while an exclusive bound means that the boundary
- point is not included in the range.
+ Every non-empty range has two bounds, the lower bound and the upper
+ bound. All points between these values are included in the range. An
+ inclusive bound means that the boundary point itself is included in
+ the range as well, while an exclusive bound means that the boundary
+ point is not included in the range.
+
- An inclusive lower bound is represented by [
- while an exclusive lower bound is represented
- by ( (see
- and below). Likewise, an inclusive
- upper bound is represented by ], while an
- exclusive upper bound is represented by ).
+ In the text form of a range, an inclusive lower bound is represented by
+ [
while an exclusive lower bound is
+ represented by (
. Likewise, an inclusive upper bound is represented by
+ ]
, while an exclusive upper bound is
+ represented by )
.
+ (See for more details.)
+
- Functions lower_inc
- and upper_inc test the inclusivity of the lower
- and upper bounds of a range, respectively.
+ The functions lower_inc
+ and upper_inc test the inclusivity of the lower
+ and upper bounds of a range value, respectively.
-
Infinite (unbounded) Ranges
+
Infinite (Unbounded) Ranges
+
- The lower bound of a range can be omitted, meaning that all points
- less (or equal to, if inclusive) than the upper bound are included
- in the range. Likewise, if the upper bound of the range is omitted,
- then all points greater than (or equal to, if omitted) the lower
- bound are included in the range. If both lower and upper bounds are
- omitted, all points are considered to be in the range.
+ The lower bound of a range can be omitted, meaning that all points less
+ than the upper bound are included in the range. Likewise, if the upper
+ bound of the range is omitted, then all points greater than the lower bound
+ are included in the range. If both lower and upper bounds are omitted, all
+ values of the element type are considered to be in the range.
+
+ This is equivalent to considering that the lower bound is minus
+ infinity, or the upper bound is plus infinity
,
+ respectively. But note that these infinite values are never values of
+ the range's element type, and can never be part of the range. (So there
+ is no such thing as an inclusive infinite bound — if you try to
+ write one, it will automatically be converted to an exclusive bound.)
+
+
+ Also, some element types have a notion of infinity>, but that
+ is just another value so far as the range type mechanisms are concerned.
+ For example, in timestamp ranges, [today,]> means the same
+ thing as [today,)>. But [today,infinity]> means
+ something different from [today,infinity)> — the latter
+ excludes the special timestamp> value infinity>.
+
+
- Functions lower_inf
- and upper_inf test the range for infinite lower
- and upper bounds of a range, respectively.
+ The functions lower_inf
+ and upper_inf test for infinite lower
+ and upper bounds of a range, respectively.
-
Input/Output
+
Range Input/Output
+
- The input follows one of the following patterns:
+ The input for a range value must follow one of the following patterns:
(lower-bound,upper-bound)
(lower-bound,upper-bound]
[lower-bound,upper-bound]
empty
- Notice that the final pattern is empty, which
- represents an empty range (a range that contains no points).
+ The parentheses or brackets indicate whether the lower and upper bounds
+ are exclusive or inclusive, as described previously.
+ Notice that the final pattern is empty, which
+ represents an empty range (a range that contains no points).
+
- The lower-bound may be either a string
- that is valid input for the sub-type, or omitted (to indicate no
- lower bound); and upper-bound may be
- either a string that is valid input for the sub-type, or omitted (to
- indicate no upper bound).
+ The lower-bound may be either a string
+ that is valid input for the subtype, or empty to indicate no
+ lower bound. Likewise, upper-bound may be
+ either a string that is valid input for the subtype, or empty to
+ indicate no upper bound.
+
- Either the lower-bound or
- the upper-bound may be quoted
- using "" (double quotation marks), which will allow
- special characters such as ",". Within quotation
- marks, "\" (backslash) serves as an escape
- character.
+ Each bound value can be quoted using " (double quote)
+ characters. This is necessary if the bound value contains parentheses,
+ brackets, commas, double quotes, or backslashes, since these characters
+ would otherwise be taken as part of the range syntax. To put a double
+ quote or backslash in a quoted bound value, precede it with a
+ backslash. (Also, a pair of double quotes within a double-quoted bound
+ value is taken to represent a double quote character, analogously to the
+ rules for single quotes in SQL literal strings.) Alternatively, you can
+ avoid quoting and use backslash-escaping to protect all data characters
+ that would otherwise be taken as range syntax. Also, to write a bound
+ value that is an empty string, write "", since writing
+ nothing means an infinite bound.
+
- The choice between the other input formats affects the inclusivity
- of the bounds. See .
+ Whitespace is allowed before and after the range value, but any whitespace
+ between the parentheses or brackets is taken as part of the lower or upper
+ bound value. (Depending on the element type, it might or might not be
+ significant.)
+
+
+ These rules are very similar to those for writing field values in
+ composite-type literals. See for
+ additional commentary.
+
+
+
Examples:
--- includes point 3, does not include point 7, and does include all points in between
-select '[3,7)'
+-- includes 3, does not include 7, and does include all points in between
+select '[3,7)'::int4range;
-- does not include either 3 or 7, but includes all points in between
-select '(3,7)'
+select '(3,7)'::int4range;
-- includes only the single point 4
-select '[4,4]'
+select '[4,4]'::int4range;
Constructing Ranges
+
- Each range type has a constructor by the same name. The constructor
+ Each range type has a constructor function with the same name as the range
+ type. Using the constructor function is frequently more convenient than
+ writing a range literal constant, since it avoids the need for extra
+ quoting of the bound values. The constructor function
accepts from zero to three arguments. The zero-argument form
constructs an empty range; the one-argument form constructs a
- singleton range; the two-argument form constructs a range
- in [ ) form; and the three-argument form
- constructs a range in a form specified by the third argument. For
- example:
+ singleton range; the two-argument form constructs a range in
+ standard form (lower bound inclusive, upper bound exclusive);
+ and the three-argument form constructs a range in a form specified by the
+ third argument. The third argument must be one of the strings
+ ()
,
+ (]
,
+ [)
, or
+ []
.
+ For example:
+
-- Three-argument form: lower bound, upper bound, and third argument indicating
--- inclusivity/exclusivity of bounds (if omitted, defaults to '[)').
+-- inclusivity/exclusivity of bounds.
SELECT numrange(1.0, 14.0, '(]');
--- The int4range input will exclude the lower bound and include the upper bound; but the
--- resulting output will appear in the canonical form; see .
+-- If the third argument is omitted, '[)' is assumed.
+SELECT numrange(1.0, 14.0);
+
+-- Although '(]' is specified here, on display the value will be converted to
+-- canonical form, since int8range is a discrete range type (see below).
SELECT int8range(1, 14, '(]');
--- Single argument form constructs a singleton range; that is a range consisting of just
--- one point.
+-- Using NULL for either bound causes the range to be unbounded on that side.
+SELECT numrange(NULL, 2.2);
+
+-- Single argument constructs a singleton range; that is a range consisting of
+-- just one point.
SELECT numrange(11.1);
--- Zero-argument form constructs and empty range.
+-- Zero-argument form constructs an empty range.
SELECT numrange();
-
--- Using NULL for a bound causes the range to be unbounded on that side; that is, negative
--- infinity for the lower bound or positive infinity for the upper bound.
-SELECT numrange(NULL,2.2);
Discrete Range Types
+
- Discrete ranges are those that have a
- defined canonical function. Loosely speaking, a
- discrete range has a sub-type with a well-defined "step";
- e.g. INTEGER or DATE.
+ A discrete range is one whose element type has a well-defined
+ step
, such as INTEGER or DATE.
+ In these types two elements can be said to be adjacent, since there are
+ no valid values between them. This contrasts with continuous ranges,
+ where it's always (or almost always) possible to identify other element
+ values between two given values. For example, a range over the
+ NUMERIC> type is continuous, as is a range over TIMESTAMP>.
+ (Even though TIMESTAMP> has limited precision, and so could
+ theoretically be treated as discrete, it's better to consider it continuous
+ since the step size is normally not of interest.)
+
- The canonical function should take an input range
- value, and return an equal range value that may have a different
- formatting. For instance, the integer range [1,
- 7] could be represented by the equal integer
- range [1, 8). The two values are equal because
- there are no points within the integer domain
- between 7 and 8, so not
- including the end point 8 is the same as
- including the end point 7. The canonical output
- for two values that are equal, like [1, 7]
- and [1, 8), must be equal. It doesn't matter
- which representation you choose to be the canonical one, as long as
- two equal values with different formattings are always mapped to the
- same value with the same formatting. If the canonical function is
- not specified, then ranges with different formatting
- (e.g. [1, 7] and [1, 8)) will
- always be treated as unequal.
+ Another way to think about a discrete range type is that there is a clear
+ idea of a next> or previous> value for each element value.
+ Knowing that, it is possible to convert between inclusive and exclusive
+ representations of a range's bounds, by choosing the next or previous
+ element value instead of the one originally given.
+ For example, in an integer range type [4,8]> and
+ (3,9)> denote the same set of values; but this would not be so
+ for a range over numeric.
+
- For types such as NUMRANGE, this is not possible,
- because there are always points in between two
- distinct NUMERIC values.
+ A discrete range type should have a canonicalization>
+ function that is aware of the desired step size for the element type.
+ The canonicalization function is charged with converting values of the
+ range type to have consistently inclusive or exclusive bounds.
+ The canonicalization function takes an input range value, and
+ must return an equivalent range value that may have a different
+ formatting. The canonical output for two values that are equivalent, like
+ [1, 7] and [1, 8), must be identical.
+ It doesn't matter which representation you choose to be the canonical one,
+ so long as two equivalent values with different formattings are always
+ mapped to the same value with the same formatting. If a canonicalization
+ function is not specified, then ranges with different formatting
+ will always be treated as unequal, even though they might represent the
+ same set of values.
+
- The built-in range
- types INT4RANGE, INT8RANGE,
- and DATERNAGE all use a canonical form that includes
- the lower bound and excludes the upper bound; that is, [
- ). User-defined ranges can use other conventions, however.
+ The built-in range types INT4RANGE, INT8RANGE,
+ and DATERANGE all use a canonical form that includes
+ the lower bound and excludes the upper bound; that is,
+ [). User-defined range types can use other conventions,
+ however.
Defining New Range Types
+
- Users can define their own range types. The most common reason to do
- this is to use ranges where the subtype is not among the built-in
- range types, e.g. a range of type FLOAT (or, if the
- subtype itself is a user-defined type).
-
- For example: to define a new range type of sub-type DOUBLE PRECISION:
+ Users can define their own range types. The most common reason to do
+ this is to use ranges over subtypes not provided among the built-in
+ range types.
+ For example, to define a new range type of subtype DOUBLE
+ PRECISION:
+
CREATE TYPE FLOATRANGE AS RANGE (
SUBTYPE = DOUBLE PRECISION
SELECT '[1.234, 5.678]'::floatrange;
- Because DOUBLE PRECISION has no meaningful "step", we
- do not define a canonical
- function. See for more
- information.
+
+ Because DOUBLE PRECISION has no meaningful
+ step
, we do not define a canonicalization
+ function.
+
+
+ Defining your own range type also allows you to specify a different
+ operator class or collation to use, so as to change the sort ordering
+ that determines which values fall into a given range. You might also
+ choose to use a different canonicalization function, either to change
+ the displayed format or to modify the effective step size>.
+
- Defining your own range type also allows you to specify a different
- operator class or collation to use (which affects the points that
- fall between the range boundaries), or a different canonicalization
- function.
+ See for more information about creating
+ range types.
+
Indexing
+
- gist
+ GiST index
-
Indexing
+
- GiST indexes can be applied to a table containing a range type. For instance:
+ GiST indexes can be applied to columns of range types. For instance:
CREATE INDEX reservation_idx ON reservation USING gist (during);
- This index may speed up queries
- involving &&
- (overlaps), @> (contains), and all the boolean
- operators found in this
- table: .
+ This index may speed up queries
+ involving &&
+ (overlaps), @> (contains), and other boolean
+ operators listed in .
+
Constraints on Ranges
+
exclude
-
Constraints on Ranges
+
While UNIQUE is a natural constraint for scalar
values, it is usually unsuitable for range types. Instead, an
exclusion constraint is often more appropriate
(see CREATE TABLE
... CONSTRAINT ... EXCLUDE). Exclusion constraints allow the
- specification of constraints such as "non-overlapping" on a range
- type. For example:
+ specification of constraints such as non-overlapping
on a
+ range type. For example:
+
ALTER TABLE reservation
- ADD EXCLUDE USING gist (during WITH &&);
+ ADD EXCLUDE USING gist (during WITH &&);
+
That constraint will prevent any overlapping values from existing
in the table at the same time:
+
-INSERT INTO reservation VALUES
- ( '[2010-01-01 11:30, 2010-01-01 13:00)' );
--- Result: INSERT 0 1
-INSERT INTO reservation VALUES
- ( '[2010-01-01 14:45, 2010-01-01 15:45)' );
--- Result:
--- ERROR: conflicting key value violates exclusion constraint "reservation_during_excl"
--- DETAIL: Key (during)=([ 2010-01-01 14:45:00, 2010-01-01 15:45:00 )) conflicts with
--- existing key (during)=([ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )).
+INSERT INTO reservation VALUES
+ ( 1108, '[2010-01-01 11:30, 2010-01-01 13:00)' );
+INSERT 0 1
+
+INSERT INTO reservation VALUES
+ ( 1108, '[2010-01-01 14:45, 2010-01-01 15:45)' );
+ERROR: conflicting key value violates exclusion constraint "reservation_during_excl"
+DETAIL: Key (during)=([ 2010-01-01 14:45:00, 2010-01-01 15:45:00 )) conflicts
+with existing key (during)=([ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )).
+
- Combine range types and exclusion constraints
- with btree_gist for maximum
- flexibility defining
- constraints. After btree_gist is installed, the
- following constraint will prevent overlapping ranges only if the
- meeting room numbers are equal:
+ You can use the btree_gist>
+ extension to define exclusion constraints on plain scalar datatypes, which
+ can then be combined with range exclusions for maximum flexibility. For
+ example, after btree_gist is installed, the following
+ constraint will reject overlapping ranges only if the meeting room numbers
+ are equal:
CREATE TABLE room_reservation
(
room TEXT,
during TSRANGE,
- EXCLUDE USING gist (room WITH =, during WITH &&)
+ EXCLUDE USING gist (room WITH =, during WITH &&)
);
INSERT INTO room_reservation VALUES
( '123A', '[2010-01-01 14:00, 2010-01-01 15:00)' );
--- Result: INSERT 0 1
+INSERT 0 1
+
INSERT INTO room_reservation VALUES
( '123A', '[2010-01-01 14:30, 2010-01-01 15:30)' );
--- Result:
--- ERROR: conflicting key value violates exclusion constraint "room_reservation_room_during_excl"
--- DETAIL: Key (room, during)=(123A, [ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )) conflicts with
--- existing key (room, during)=(123A, [ 2010-01-01 14:00:00, 2010-01-01 15:00:00 )).
+ERROR: conflicting key value violates exclusion constraint "room_reservation_room_during_excl"
+DETAIL: Key (room, during)=(123A, [ 2010-01-01 14:30:00, 2010-01-01 15:30:00 )) conflicts with
+existing key (room, during)=(123A, [ 2010-01-01 14:00:00, 2010-01-01 15:00:00 )).
+
INSERT INTO room_reservation VALUES
( '123B', '[2010-01-01 14:30, 2010-01-01 15:30)' );
--- Result: INSERT 0 1
-
+INSERT 0 1