F.26. seg — 用于线段或浮点区间的数据类型 #
该模块实现了一个用于表示线段或浮点区间的seg数据类型。seg可以表示区间端点中的不确定性,因此特别适合表示实验室测量结果。
F.26.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.26.2. 语法
区间的外部表示由一个或两个浮点数通过范围操作符(.. 或 ...)连接而成。另一种写法是指定中心点再加减一个偏差值。还可以存储可选的确定性指示符(<、>
和 ~)。(不过,所有内置操作符都会忽略这些确定性指示符。)
在下表中,x、y和delta表示浮点数。x和y前面可以带确定性指示符,而delta不可以。
表 F.31. seg 的外部表示
| 单个值(零长度区间) |
| 从 x 到 y 的区间
|
| 从 x - delta 到
x + delta 的区间
|
| 下界为 x、上界开放的区间
|
.. | 上界为 x、下界开放的区间
|
表 F.32. 有效 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.26.3. 精度
seg值在内部存储为一对 32 位浮点数。这意味着有效位数超过 7 位的数字会被截断。
有效位数不超过 7 位的数字会保留其原始精度。也就是说,如果你的查询返回 0.00,你可以确信末尾的零不是格式化造成的假象,而是反映了原始数据的精度。前导零的个数不影响精度:值 0.0067 被认为只有 2 位有效数字。
F.26.4. 用法
seg模块包含适用于seg值的一个 GiST 索引操作符类。该 GiST 操作符类支持的操作符包括:
[a, b] << [c, d] Is left of[a, b] 完全位于 [c, d] 的左边。也就是说,当 b < c 时 [a, b] << [c, d] 为真,否则为假
[a, b] >> [c, d] Is right of[a, b] 完全位于 [c, d] 的右边。也就是说,当 a > d 时 [a, b] >> [c, d] 为真,否则为假
[a, b] &< [c, d] Overlaps or is left of这个操作符或许更适合读作“不向右延伸”。当 b <= d 时它为真。
[a, b] &> [c, d] Overlaps or is right of这个操作符或许更适合读作“不向左延伸”。当 a >= c 时它为真。
[a, b] = [c, d] Same as线段 [a, b] 与 [c, d] 完全相同,即 a = c 且 b = d
[a, b] && [c, d] Overlaps线段 [a, b] 与 [c, d] 重叠。
[a, b] @> [c, d] Contains线段 [a, b] 包含线段 [c, d],即 a <= c 且 b >= d
[a, b] <@ [c, d] Contained in线段 [a, b] 被包含于 [c, d] 中,即 a >= c 且 b <= d
(在 PostgreSQL 8.2 之前,包含操作符 @> 和 <@ 分别称为 @ 和 ~。这些名称仍然可用,但已弃用,最终将被删除。请注意,旧名称与核心几何数据类型以前采用的约定正好相反!)
还提供标准的 B-树操作符,例如
[a, b] < [c, d] 小于 [a, b] > [c, d] 大于
这些操作符除了排序之外,在实际用途上没有太大意义。它们首先比较 (a) 与 (c),如果相等,再比较 (b) 与 (d)。这在大多数情况下会产生相当好的排序效果;如果希望对这种类型使用 ORDER BY,这会很有用。
F.26.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.26.6. 致谢
原作者:Gene Selkov, Jr. <selkovjr@mcs.anl.gov>,阿贡国家实验室数学与计算机科学部。
我首先要感谢 Joe Hellerstein 教授 (http://db.cs.berkeley.edu/~jmh/),他为我阐明了 GiST (http://gist.cs.berkeley.edu/) 的要旨。我同样感谢过去和现在所有的 Postgres 开发者,他们使我得以创造自己的世界并在其中不受打扰地生活。我还要感谢阿贡实验室以及美国能源部,多年来始终如一地支持我的数据库研究。