63.2. B-树操作符类的行为 #
正如表 38.2 所示,B-树操作符类必须提供五个比较操作符,<、<=、=、>= 和
>。人们可能会以为 <> 也应当属于操作符类的一部分,但事实并非如此,因为在索引搜索中使用
<> 的 WHERE 子句几乎毫无用处。(出于某些目的,规划器会把 <> 视为与 B-树操作符类相关联;但规划器是通过 = 操作符的求反器链接找到该操作符,而不是从
pg_amop 中找到它。)
当多种数据类型共享几乎相同的排序语义时,可以将它们的操作符类归入同一个操作符族。这样做的好处是,规划器就能够对跨类型比较作出推理。族中的每个操作符类都应当包含对应其输入数据类型的单类型操作符(以及相关支持函数),而跨类型比较操作符及其支持函数则作为该族中的“松散”成员存在。建议在族中包含一套完整的跨类型操作符,从而确保规划器能够表示它根据传递性推导出来的任意比较条件。
B-树操作符族必须满足一些基本假设:
=操作符必须是一种等价关系;也就是说,对于该数据类型的所有非 null 值A、B、C:A=A为真(自反律)如果
A=B,则B=A(对称律)如果
A=B且B=C,则A=C(传递律)
<操作符必须是一种强排序关系;也就是说,对于所有非 null 值A、B、C:A<A为假(非自反律)如果
A<B且B<C,则A<C(传递律)
此外,该顺序还是全序的;也就是说,对于所有非 null 值
A、B:A<B、A=B以及B<A中恰有一个为真(三分律)
(三分律当然也正是定义比较支持函数的依据。)
另外三个操作符都可以用 = 和
< 以显而易见的方式定义,并且必须与它们保持一致。
对于支持多种数据类型的操作符族,当 A、B、C
取自该族中的任意数据类型时,上述定律都必须成立。传递律最难保证,因为在跨类型情况下,它要求两种或三种不同操作符的行为彼此一致。举例来说,不能把
float8 和 numeric
放进同一个操作符族,至少在当前这种语义下不行:会先将
numeric 值转换为 float8,以便与
float8 比较。由于
float8 精度有限,这意味着会存在不同的
numeric 值都与同一个 float8
值比较为相等,于是传递律就会失效。
对于多数据类型操作符族,另一个要求是:在族内数据类型之间定义的任何隐式或二进制强制转换,都不得改变相应的排序顺序。
很容易理解为什么 B-树索引要求这些定律在单一数据类型内部成立:没有这些定律,就不存在可用于排列键的顺序。此外,使用不同数据类型比较键的索引搜索,也要求跨两种数据类型的比较行为合理一致。把这些要求扩展到一个族内的三种或更多数据类型,虽然并非 B-树索引机制本身的严格要求,但规划器会出于优化目的依赖它们。