PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

F.39. seg — 用于线段或浮点区间的一种数据类型 #

F.39.1. 设计缘由
F.39.2. 语法
F.39.3. 精度
F.39.4. 用法
F.39.5. 注记
F.39.6. 致谢

该模块实现数据类型 seg,用于表示线段或浮点区间。seg 可以表示区间端点的不确定性,这使它特别适合表示实验室测量数据。

该模块被视为“可信”模块,也就是说,拥有当前数据库 CREATE 权限的非超级用户也可以安装它。

F.39.1. 设计缘由 #

测量的几何结构通常比数值连续统中的一个点更复杂。测量结果通常是该连续统中界限略显模糊的一段。由于不确定性与随机性,测量结果表现为区间;此外,被测量的值本身也可能自然就是一个表示某种状态的区间,例如某种蛋白质的稳定温度范围。

凭常识判断,把这类数据作为区间存储比作为数字对存储更方便。在实践中,这在大多数应用中甚至也更高效。

再进一步从常识出发,界限的模糊性意味着使用传统数值数据类型会导致一定的信息丢失。考虑这种情况:你的仪器读数为 6.50,你把这个读数输入数据库。取回时你得到什么?请看:

test=> select 6.50 :: float8 as "pH";
 pH
---
6.5
(1 row)

在测量的世界里,6.50 与 6.5 不同。有时这种差别甚至至关重要。实验者通常会写下(并发表)他们信任的数字。6.50 实际上是一个模糊区间,包含在一个更大、更模糊的区间 6.5 之中,两者的中心点是它们(可能)唯一的共同特征。我们当然不希望如此不同的数据项看起来一模一样。

结论?拥有一种能够以任意可变精度记录区间界限的特殊数据类型是件好事。所谓可变,是指每个数据元素记录自己的精度。

请看:

test=> select '6.25 .. 6.50'::seg as "pH";
          pH
------------
6.25 .. 6.50
(1 row)

F.39.2. 语法 #

区间的外部表示由一个或两个浮点数通过范围操作符(.. 或 ...)连接构成。也可以指定为中心点加减一个偏差。可选的确定性指示符(<、> 或 ~)也可以被存储。(不过,所有内置操作符都会忽略确定性指示符。)Table F.29 概述了允许的表示形式;Table F.30 给出了一些示例。

在 Table F.29 中,x、y 与 delta 表示浮点数。x 与 y(delta 不可以)前面可以带有确定性指示符。

Table F.29. seg 的外部表示

x单个值(零长度区间)
x .. y从 x 到 y 的区间
x (+-) delta从 x - delta 到 x + delta 的区间
x ..下界为 x 的开区间
.. x上界为 x 的开区间

Table F.30. 有效 seg 输入示例

5.0 创建一个零长度线段(不妨说是一个点)
~5.0 创建一个零长度线段并在数据中记录 ~。~ 会被 seg 操作忽略,但作为注释保留。
<5.0 在 5.0 处创建一个点。< 被忽略,但作为注释保留。
>5.0 在 5.0 处创建一个点。> 被忽略,但作为注释保留。
5(+-)0.3 创建区间 4.7 .. 5.3。注意 (+-) 记法不会被保留。
50 .. 大于或等于 50 的一切值
.. 0小于或等于 0 的一切值
1.5e-2 .. 2E-2 创建区间 0.015 .. 0.02
1 ... 2 与 1...2、1 .. 2 或 1..2 相同 (范围操作符周围的空格被忽略)

由于 ... 操作符在数据来源中被广泛使用,它被允许作为 .. 操作符的替代写法。遗憾的是,这带来了一个解析歧义:无法确定 0...23 中的上界究竟是 23 还是 0.23。解决方法是要求 seg 输入中所有数字在小数点之前至少有一位数字。

作为一种合理性检查,seg 拒绝下界大于上界的区间,例如 5 .. 2。

F.39.3. 精度 #

seg 值在内部存储为一对 32 位浮点数。这意味着有效数字超过 7 位的数字会被截断。

有效数字不超过 7 位的数字保留其原始精度。也就是说,如果你的查询返回 0.00,你可以确定尾部的零不是格式化的产物:它们反映了原始数据的精度。前导零的数量不影响精度:值 0.0067 被认为只有 2 位有效数字。

F.39.4. 用法 #

seg 模块包含面向 seg 值的 GiST 索引操作符类。GiST 操作符类支持的操作符见 Table F.31。

Table F.31. Seg GiST 操作符

操作符

描述

seg << seg → boolean

第一个 seg 是否完全位于第二个的左侧?当 b < c 时 [a, b] << [c, d] 为真。

seg >> seg → boolean

第一个 seg 是否完全位于第二个的右侧?当 a > d 时 [a, b] >> [c, d] 为真。

seg &< seg → boolean

第一个 seg 是否未延伸到第二个的右侧?当 b <= d 时 [a, b] &< [c, d] 为真。

seg &> seg → boolean

第一个 seg 是否未延伸到第二个的左侧?当 a >= c 时 [a, b] &> [c, d] 为真。

seg = seg → boolean

两个 seg 是否相等?

seg && seg → boolean

两个 seg 是否重叠?

seg @> seg → boolean

第一个 seg 是否包含第二个?

seg <@ seg → boolean

第一个 seg 是否包含于第二个?


除上述操作符外,Table 9.1 中所示的常规比较操作符也适用于 seg 类型。这些操作符先比较 (a) 与 (c),如果相等再比较 (b) 与 (d)。这在大多数情况下能得到相当好的排序效果,如果你想对该类型使用 ORDER BY,这会很有用。

F.39.5. 注记 #

用法示例请参见回归测试 sql/seg.sql。

将 (+-) 转换为普通范围的机制在确定边界的有效数字位数方面并不完全准确。例如,如果所得区间包含 10 的幂,它会为下界增加一位额外数字:

postgres=> select '10(+-)1'::seg as seg;
      seg
---------
9.0 .. 11             -- should be: 9 .. 11

R 树索引的性能可能在很大程度上取决于输入值的初始顺序。对输入表按 seg 列排序可能非常有帮助;示例请参见脚本 sort-segments.pl。

F.39.6. 致谢 #

原始作者:Gene Selkov, Jr. ,阿贡国家实验室数学与计算机科学部。

我首先要感谢 Joe Hellerstein 教授(https://dsf.berkeley.edu/jmh/)阐明了 GiST(http://gist.cs.berkeley.edu/)的精髓。我也要感谢过去与现在的所有 Postgres 开发者,让我能够创造自己的世界并安然居于其中。同时我感谢阿贡实验室与美国能源部多年来对我数据库研究的坚定支持。