8.17. 范围类型

范围类型是表示某种元素类型的值范围的数据类型(称为范围的* subtype *)。例如,范围timestamp可用于表示会议室 sched 的时间范围。在这种情况下,数据类型为tsrange(“时间戳范围”的缩写),而timestamp是子类型。子类型必须具有总 Sequences,以便可以很好地定义元素值是在值范围之内,之前还是之后。

范围类型之所以有用,是因为它们在一个范围值中表示许多元素值,并且因为可以清楚地表示诸如重叠范围之类的概念。将时间和日期范围用于计划目的是最明显的例子;但是价格范围,仪器的测量范围等等也可能有用。

8 .17.1. 内置范围类型

PostgreSQL 带有以下内置范围类型:

  • int4rangeinteger的范围

  • int8rangebigint的范围

  • numrangenumeric的范围

  • tsrangetimestamp without time zone的范围

  • tstzrangetimestamp with time zone的范围

  • daterangedate的范围

另外,您可以定义自己的范围类型。有关更多信息,请参见CREATE TYPE

8.17.2. Examples

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);

-- Extract the upper bound
SELECT upper(int8range(15, 25));

-- Compute the intersection
SELECT int4range(10, 20) * int4range(15, 25);

-- Is the range empty?
SELECT isempty(numrange(1, 5));

有关范围类型的运算符和函数的完整列表,请参见Table 9.50Table 9.51

8 .17.3. 包容性和排他性界限

每个非空范围都有两个边界,即下限和上限。这些值之间的所有点都包括在范围内。包含边界意味着边界点本身也包含在范围内,而排斥边界意味着边界点不包含在范围内。

在范围的文本形式中,包含的下限由“ [”表示,而排除的下界由“ (”表示。同样,包含上限由“ ]”表示,排他上限由“ )”表示。 (有关更多详细信息,请参见Section 8.17.5。)

函数lower_incupper_inc分别测试范围值的上下边界的包容性。

8 .17.4. 无限(无界)范围

范围的下限可以省略,这意味着小于上限的所有值都包含在该范围内,例如(,3]。同样,如果省略范围的上限,则所有大于下限的值都将包含在范围内。如果上限和下限均被忽略,则元素类型的所有值均视为在该范围内。将缺少的界限指定为包含式将自动转换为排斥式,例如[,]转换为(,)。您可以将这些丢失的值视为/ -infinity,但是它们是特殊的范围类型值,并且被认为超出了任何范围元素类型的/ -infinity 值。

具有“无限”概念的元素类型可以将它们用作显式绑定值。例如,在时间戳范围内,[today,infinity)排除特殊的timestampinfinity,而[today,infinity]包括它,[today,)[today,]也是如此。

函数lower_infupper_inf分别测试范围的无限下限和上限。

8 .17.5. 范围 Importing/输出

范围值的 Importing 必须遵循以下模式之一:

(lower-bound,upper-bound)
(lower-bound,upper-bound]
[lower-bound,upper-bound)
[lower-bound,upper-bound]
empty

如前所述,括号或方括号指示上限和下限是互斥的还是包含的。请注意,最后一个模式是empty,它表示一个空范围(不包含点的范围)。

  • lower-bound 可以是该子类型的有效 Importing 字符串,也可以是空字符串以表示没有下限。同样, upper-bound *可以是该子类型的有效 Importing 字符串,也可以为空以表示没有上限。

每个绑定值都可以使用"(双引号)字符引起来。如果绑定值包含括号,方括号,逗号,双引号或反斜杠,则这是必需的,因为否则这些字符将作为范围语法的一部分。要将双引号或反斜杠放在带引号的绑定值中,请在其前面加上反斜杠。 (此外,类似于 SQLLiterals 字符串中单引号的规则,在双引号的绑定值内使用一对双引号表示一个双引号字符.)或者,可以避免使用引号并使用反斜杠转义来保护字符所有其他将用作范围语法的数据字符。另外,要写入一个空字符串的绑定值,请写入"",因为什么都不写就意味着无限的绑定。

范围值之前和之后都允许有空格,但是括号或方括号之间的任何空格都将作为下限值或上限值的一部分。 (取决于元素类型,它可能是重要的,也可能不是重要的.)

Note

这些规则与以复合类型 Literals 编写字段值的规则非常相似。有关其他 Comment,请参见Section 8.16.6

Examples:

-- 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)'::int4range;

-- includes only the single point 4
SELECT '[4,4]'::int4range;

-- includes no points (and will be normalized to 'empty')
SELECT '[4,4)'::int4range;

8 .17.6. 构造范围

每个范围类型都有一个与范围类型同名的构造函数。使用构造函数通常比编写范围 Literals 常量更方便,因为它避免了对绑定值的额外引用。构造函数接受两个或三个参数。二元参数形式构造标准范围内的范围(下限包括在内,上限不包括在内),而三参数形式构造范围为第三个参数所指定形式的范围。第三个参数必须是字符串“ ()”,“ (]”,“ [)”或“ []”之一。例如:

-- The full form is: lower bound, upper bound, and text argument indicating
-- inclusivity/exclusivity of bounds.
SELECT numrange(1.0, 14.0, '(]');

-- 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, '(]');

-- Using NULL for either bound causes the range to be unbounded on that side.
SELECT numrange(NULL, 2.2);

8 .17.7. 离散范围类型

离散范围是指其元素类型具有定义明确的“步骤”的范围,例如integerdate。在这些类型中,当两个元素之间没有有效值时,可以说它们是相邻的。这与连续范围形成对比,连续范围总是(或几乎总是)可以识别两个给定值之间的其他元素值。例如,numeric类型上的范围是连续的,timestamp类型上的范围也是连续的。 (尽管timestamp的精度有限,因此理论上可以将其视为离散的,但最好将其视为连续的,因为通常不关心步长.)

考虑离散范围类型的另一种方法是,每个元素值都有一个“下一个”或“上一个”值的清晰概念。知道这一点,可以通过选择下一个或上一个元素值而不是最初指定的值来在范围边界的包含表示和排除表示之间进行转换。例如,在整数范围内,类型[4,8](3,9)表示同一组值;但是对于数字范围则不是这样。

离散范围类型应具有规范化功能,该功能可以了解元素类型所需的步长。规范化功能负责将范围类型的等效值转换为具有相同的表示形式,尤其是始终如一的包含或排除范围。如果未指定规范化函数,则即使格式不同的范围实际上可能表示同一组值,也始终将其视为不相等。

内置范围类型int4rangeint8rangedaterange均使用规范形式,该规范形式包括下限但不包括上限;就是[)。但是,用户定义的范围类型可以使用其他约定。

8 .17.8. 定义新的范围类型

用户可以定义自己的范围类型。这样做的最常见原因是对内置范围类型中未提供的子类型使用范围。例如,要定义子类型float8的新范围类型:

CREATE TYPE floatrange AS RANGE (
    subtype = float8,
    subtype_diff = float8mi
);

SELECT '[1.234, 5.678]'::floatrange;

由于float8没有有意义的“步骤”,因此在此示例中我们没有定义规范化函数。

定义自己的范围类型还可以使您指定要使用的其他子类型 B-tree 运算符类或排序规则,以便更改确定哪些值属于给定范围的排序 Sequences。

如果子类型被认为具有离散值而不是连续值,则CREATE TYPE命令应指定canonical函数。规范化函数采用 Importing 范围值,并且必须返回可能具有不同范围和格式的等效范围值。代表同一组值的两个范围的规范输出(例如,整数范围[1, 7][1, 8))必须相同。只要选择将两种表示形式相同的值始终 Map 为相同的值,则选择哪种表示形式都无所谓。除了调整包含/排除边界格式之外,在所需步长大于子类型能够存储的步长的情况下,规范化函数还可以舍入边界值。例如,可以将timestamp以上的范围类型定义为一个小时的步长,在这种情况下,规范化功能将需要舍入不是一个小时数倍的范围,或者可能会抛出错误。

此外,任何打算与 GiST 或 SP-GiST 索引一起使用的范围类型都应定义一个子类型差或subtype_diff函数。 (如果没有subtype_diff,索引仍然可以工作,但是它的效率可能比提供差动函数的效率低得多.)子类型差动函数接受子类型的两个 Importing 值,并返回它们的差(即* X Y *)表示为float8值。在上面的示例中,可以使用作为常规float8减运算符基础的函数float8mi;但对于任何其他子类型,则需要某种类型转换。也可能需要一些关于如何将差异表示为数字的 Creating 性思考。 subtype_diff函数应尽可能地符合所选操作符类和排序规则所隐含的排序 Sequences;也就是说,根据排序 Sequences,只要第一个参数大于第二个参数,其结果就应该为正。

subtype_diff函数的一个不太简化的示例是:

CREATE FUNCTION time_subtype_diff(x time, y time) RETURNS float8 AS
'SELECT EXTRACT(EPOCH FROM (x - y))' LANGUAGE sql STRICT IMMUTABLE;

CREATE TYPE timerange AS RANGE (
    subtype = time,
    subtype_diff = time_subtype_diff
);

SELECT '[11:10, 23:00]'::timerange;

有关创建范围类型的更多信息,请参见CREATE TYPE

8.17.9. Indexing

可以为范围类型的表列创建 GiST 和 SP-GiST 索引。例如,要创建 GiST 索引:

CREATE INDEX reservation_idx ON reservation USING GIST (during);

GiST 或 SP-GiST 索引可以加快涉及以下范围运算符的查询:=&&<@@><<>>-|-&<&>(有关更多信息,请参见Table 9.50)。

另外,可以为范围类型的表列创建 B 树索引和哈希索引。对于这些索引类型,基本上唯一有用的范围操作是相等。对于范围值定义了 B 树排序 Sequences,并带有相应的<>运算符,但是该 Sequences 相当随意,通常在现实世界中没有用。范围类型的 B 树和哈希支持主要是为了允许在查询中进行内部排序和哈希,而不是创建实际索引。

8 .17.10. 范围约束

虽然UNIQUE是标量值的自然约束,但通常不适合范围类型。相反,排除约束通常更合适(请参阅创建表...约束...排除)。排除约束允许对范围类型指定约束,例如“不重叠”。例如:

CREATE TABLE reservation (
    during tsrange,
    EXCLUDE USING GIST (during WITH &&)
);

该约束将防止同时存在表中的任何重叠值:

INSERT INTO reservation VALUES
    ('[2010-01-01 11:30, 2010-01-01 15:00)');
INSERT 0 1

INSERT INTO reservation VALUES
    ('[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 11:30:00","2010-01-01 15:00:00")).

您可以使用btree_gistextensions 来定义普通标量数据类型的排除约束,然后将其与范围排除结合使用,以实现最大的灵 Active。例如,安装btree_gist后,仅当会议室号相等时,以下约束才会拒绝重叠范围:

CREATE EXTENSION btree_gist;
CREATE TABLE room_reservation (
    room text,
    during tsrange,
    EXCLUDE USING GIST (room WITH =, during WITH &&)
);

INSERT INTO room_reservation VALUES
    ('123A', '[2010-01-01 14:00, 2010-01-01 15:00)');
INSERT 0 1

INSERT INTO room_reservation VALUES
    ('123A', '[2010-01-01 14:30, 2010-01-01 15:30)');
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)');
INSERT 0 1