
# 范围类型
范围类型是表达某种元素类型（称为范围的subtype）的一个值的范围的数据类型。例如，timestamp的范围可以被用来表达一个会议室被保留的时间范围。在这种情况下，数据类型是tsrange（"timestamp range"的简写），而timestamp是其subtype。subtype必须具有一种总体的顺序，这样对于元素值是在一个范围值之内、之前还是之后就已经是明确的了。
范围类型非常有用，因为它们可以表达一种单一范围值中的多个元素值，并且可以很清晰地表达诸如范围重叠等概念，如用于时间安排的时间和日期范围，以及价格范围、仪器的量程等场景都是可以表示的。
#### 内建范围类型
有下列内建范围类型：
- int4range --- integer的范围
- int8range --- bigint的范围
- numrange --- numeric的范围
- tsrange --- 不带时区的 timestamp的范围
- tstzrange --- 带时区的 timestamp的范围
- daterange --- date的范围
此外，用户可以定义自己的范围类型，详见[CREATE TYPE](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/gaussdb-12-0547.html#ZH-CN_TOPIC_0000002200190314)。
#### 例子
```
CREATE TABLE reservation (room int, during tsrange); 
INSERT INTO reservation VALUES (1108, '[2010-01-01 14:30, 2010-01-01 15:30)');  
-- 包含 
SELECT int4range(10, 20) @> 3;  
 ?column? 
----------
 f
(1 row)
-- 判断是否重叠 
SELECT numrange(11.1, 22.2) && numrange(20.0, 30.0);  
 ?column? 
----------
 t
(1 row)
-- 抽取上界 
SELECT upper(int8range(15, 25));  
 upper 
-------
    25
(1 row)
-- 计算交集 
SELECT int4range(10, 20) * int4range(15, 25);  
 ?column? 
----------
 [15,20)
(1 row)
-- 判断范围是否为空 
SELECT isempty(numrange(1, 5)); 
 isempty 
---------
 f
(1 row)
DROP TABLE reservation;
```
范围类型上的操作符和函数的完整列表可见[范围函数和操作符](https://support.huaweicloud.com/distributed-devg-v3-gaussdb/gaussdb-12-0360.html#ZH-CN_TOPIC_0000002200190022)。
#### 包含和排除边界
每一个非空范围都有两个界限，下界和上界。上下界之间的所有点都被包括在范围内。一个包含界限意味着边界点本身也被包括在范围内，而一个排除边界意味着边界点不被包括在范围内。
在一个范围的文本形式中，一个包含下界被表达为"\["而一个排除下界被表达为"("。同样，一个包含上界被表达为"\]"而一个排除上界被表达为")"（详见[范围输入/输出]）。
函数lower_inc和upper_inc分别测试一个范围值的上下界。
#### 无限（无界）范围
一个范围的下界可以被忽略，意味着所有小于上界的值都被包括在范围中。 同样，如果范围的上界被忽略，那么所有比下界大的值都被包括在范围中。如果上下界都被忽略，该元素类型的所有值都被认为在该范围中。 规定缺失的包括界限自动转换为排除。 用户可以认为这些缺失值为 +/- 无穷大，但它们是特殊范围类型值，并且被视为超出任何范围元素类型的 +/- 无穷大值。
具有"infinity"概念的元素类型可以用它们作为显式边界值。 例如，在时间戳范围，\[today,infinity) 不包括特殊的 timestamp 值 infinity，尽管 \[today,infinity\] 包括它，就好比 \[today,) 和 \[today,\]。
函数lower_inf和upper_inf分别测试一个范围的无限上下界。
 #### 范围输入/输出
一个范围值的输入必须遵循下列模式之一：
```
(lower-bound,upper-bound) 
(lower-bound,upper-bound] 
[lower-bound,upper-bound) 
[lower-bound,upper-bound] 
empty
```
一个范围值的输出必须遵循下列模式之一：
```
[lower-bound,upper-bound) 
empty
```
圆括号或方括号指示上下界是否为排除的或者包含的。注意最后一个模式是empty，它表示一个空范围（一个不包含点的范围）。
*lower-bound* 可以是作为 subtype 的合法输入的一个字符串，或者是空表示没有下界。同样，*upper-bound*可以是作为 subtype 的合法输入的一个字符串，或者是空表示没有上界。
每个界限值可以使用"（双引号）字符引用。如果界限值包含圆括号、方括号、逗号、双引号或反斜线时，这样做是必须的，否则那些字符会被认作范围语法的一部分。要把一个双引号或反斜线放在一个被引用的界限值中，就在它前面放一个反斜线（还有，在一个双引号引用的界限值中的一对双引号表示一个双引号字符，这与 SQL 字符串中的单引号规则类似）。此外，用户可以避免引用或者使用反斜线转义来保护所有数据字符，否则它们会被当做范围语法的一部分。还有，要写一个是空字符串的界限值，则可以写成""，因为什么都不写表示一个无限界限。
范围值前后允许有空格，但是圆括号或方括号之间的任何空格会被当做上下界值的一部分（取决于元素类型，它可能是也可能不是有意义的）。
例子：
```
--包括3，不包括7之间的所有点。 
gaussdb=# SELECT '[3,7)'::int4range;  
 int4range 
-----------
 [3,7)
(1 row)
--既不包括3也不包括7之间的所有点。
gaussdb=# SELECT '(3,7)'::int4range;  
 int4range 
-----------
 [4,7)
(1 row)
--只包括单独一个点4。
gaussdb=# SELECT '[4,4]'::int4range;  
 int4range 
-----------
 [4,5)
(1 row)
--不包括点（并且将被标准化为 '空'）。 
gaussdb=# SELECT '[4,4)'::int4range; 
 int4range 
-----------
 empty
(1 row)
```
#### 构造范围
每一种范围类型都有一个与其同名的构造器函数。使用构造器函数常常比写一个范围文字常数更方便，因为它避免了对界限值的额外引用。构造器函数接受两个或三个参数。两个参数的形式以标准的形式构造一个范围（下界是包含的，上界是排除的），而三个参数的形式按照第三个参数指定的界限形式构造一个范围。第三个参数必须是下列字符串之一： "()"、 "(\]"、 "\[)"或者 "\[\]"。 例如：
```
--完整形式是：下界、上界以及指示界限包含性/排除性的文本参数。 
gaussdb=# SELECT numrange(1.0, 14.0, '(]');  
  numrange  
------------
 (1.0,14.0]
(1 row)
--如果第三个参数被忽略，则假定为 '[)'。 
gaussdb=# SELECT numrange(1.0, 14.0);  
  numrange  
------------
 [1.0,14.0)
(1 row)
--尽管这里指定了 '(]'，显示时该值将被转换成标准形式，因为int8range是一种离散范围类型。 
gaussdb=# SELECT int8range(1, 14, '(]');  
 int8range 
-----------
 [2,15)
(1 row)
--为一个界限使用NULL导致范围在那一边是无界的。 
gaussdb=# SELECT numrange(NULL, 2.2); 
 numrange 
----------
 (,2.2)
(1 row)
```
#### 离散范围类型
一种范围的元素类型具有一个良定义的"步长"，例如integer或date。在这些类型中，如果两个元素之间没有合法值，它们可以被说成是相邻。这与连续范围相反，连续范围中总是（或者几乎总是）可以在两个给定值之间标识其他元素值。例如，numeric类型之上的一个范围就是连续的，timestamp上的范围也是（尽管timestamp具有有限的精度，并且在理论上可以被当做离散的，但是可以认为它是连续的，因为通常并不关心它的步长）。
另一种考虑离散范围类型的方法是对每一个元素值都有一个清晰的"下一个"或"上一个"值。了解了这种思想之后，通过选择原来给定的下一个或上一个元素值来取代它，就可以在一个范围界限的包含和排除表达之间转换。例如，在一个整数范围类型中，\[4,8\]和(3,9)表示相同的值集合，但是对于 numeric 上的范围就不是这样。
一个离散范围类型应该具有一个正规化函数，它知道元素类型期望的步长。正规化函数负责把范围类型的相等值转换成具有相同的表达，特别是与包含或者排除界限一致。如果没有指定一个正规化函数，那么具有不同格式的范围将总是会被当作不等，即使它们实际上是表达相同的一组值。
内建的范围类型int4range、int8range和daterange都使用一种正规的形式，该形式包括下界并且排除上界，也就是\[)。不过，用户定义的范围类型可以使用其他形式。
#### 索引
此外，B-树索引可以在范围类型的表列上创建。对于这些索引类型，基本上唯一有用的范围操作就是等值。使用相应的\< 和 \>操作符，对于范围值定义有一种 B-树排序顺序，但是该顺序相当任意并且在真实世界中通常不怎么有用。范围类型的 B-树支持主要是为了允许在查询内部进行排序，而不是创建真正的索引。
