为什么 PostgreSQL 认为范围类型中的 NULL 边界与无限边界不同?

Why does PostgreSQL consider NULL boundaries in range types to be distinct from infinite boundaries?

作为序言,我不是在问 空边界和无限边界之间的区别 - 即 covered in this other question。相反,我问的是 为什么 PostgreSQL 在(据我所知)功能完全相同时区分 NULL 和无限边界。

我最近开始使用 PostgreSQL 的范围类型,我对范围类型中的 NULL 值的含义感到有点困惑。 The documentation 说:

The lower bound of a range can be omitted, meaning that all values less than the upper bound are included in the range, e.g., (,3]. Likewise, if the upper bound of the range is omitted, then all values greater than the lower bound are included in the range. If both lower and upper bounds are omitted, all values of the element type are considered to be in the range.

这向我建议,范围内的省略边界(范围类型的构造函数中指定的等效 NULL 边界)应被视为 无限 。但是,PostgreSQL 区分了 NULL 边界和无限边界。文档继续:

You can think of these missing values [in a range] as +/-infinity, but they are special range type values and are considered to be beyond any range element type's +/-infinity values.

这令人费解。 “超越无限”没有意义,因为无限值的全部意义在于 nothing 可以大于 +infinity 或小于 -infinity。这不会破坏“范围内的元素”类型的检查,但它确实引入了一个有趣的主键案例,我认为大多数人都不会想到。或者至少,没想到。

假设我们创建一个基本的table,它的唯一字段是一个日期范围,它也是主键:

CREATE TABLE public.range_test
(
    id daterange NOT NULL,
    PRIMARY KEY (id)
);

然后我们可以毫无问题地使用以下数据填充它:

INSERT INTO range_test VALUES (daterange('-infinity','2021-05-21','[]'));
INSERT INTO range_test VALUES (daterange(NULL,'2021-05-21','[]'));

选择所有数据显示我们有这两个元组:

[-infinity,2021-05-22)
(,2021-05-22)

所以这两个元组是不同的,否则就会违反主键。但同样,当我们处理构成范围的实际元素时,NULL 边界和无限边界的作用完全相同。例如,没有 date 值 X 使得 X <@ [-infinity,2021-05-22) returns 的结果与 X <@ (,2021-05-22) 的结果不同。这是有道理的,因为 NULL 值不能具有 date 类型,因此它们甚至不能与范围进行比较(并且 PostgreSQL 甚至将 daterange(NULL,'2021-05-21','[]') 中 NULL 下限的包含边界转换为一个排他边界,(,2021-05-22) 要加倍确定)。但是,为什么在每个实际方面都相同的两个范围被认为是不同的?

我还在上学的时候,我记得无意中听到一些关于“未知”和“不存在”之间区别的讨论——两个比我聪明的人在讨论为什么 NULL 值通常会引起问题,用单独的“未知”和“不存在”值替换单数 NULL 可能会解决这些问题,但当时讨论超出了我的理解范围。想到这个奇怪的功能,我想起了那个讨论。那么“未知”和“不存在”之间的区别是 PostgreSQL 将 NULL 和 +-infinity 视为不同的原因吗?如果是这样,为什么范围是唯一允许在 PostgreSQL 中进行区分的类型?如果不是,为什么 PostgreSQL 将功能等价的值视为不同的值?

Rather, I'm asking why PostgreSQL makes a distinction between NULL and infinite boundaries when (as far as I can tell) they function exactly the same.

但他们没有。 NULL 语法便利性 当用作范围的界限时,而 -infinity / infinity 是实际的 在范围域中。抽象值意味着小于/大于任何其他值,但 仍然存在(可以包括或排除)。

此外,NULL 适用于 任何 范围类型,而大多数数据类型没有像 -infinity / infinity 这样的特殊值.以integerint4range为例。

为了更好地理解,请考虑 pgsql-general 中的线程 :

This makes sense because NULL values can't have a type of date, so they can't even be compared to the range

每个 数据类型都可以是NULL,甚至是显式NOT NULL 的域。参见:

当然包括date(比如):

test=> SELECT NULL::date, pg_typeof(NULL::date);
 date | pg_typeof 
------+-----------
      | date
(1 row)

但是试图将 NULL 讨论为 value(当用作范围的边界时)是一种误导性的方法。这不是一个值。

... (and PostgreSQL even converted the inclusive boundary on the lower NULL bound in daterange(NULL,'2021-05-21','[]') to an exclusive boundary, (,2021-05-22) to be doubly sure).

同样,NULL 不被视为范围域中的值。它只是作为一种方便的语法来表达:“无界”。仅此而已。