作为前言,我并不是在问NULL边界和无限边界之间的区别是什么,这在另一个问题中已经讨论过了。相反,我在问为什么PostgreSQL在NULL和无限边界之间进行区分,而(据我所知)它们的功能完全相同。
我最近开始使用PostgreSQL的范围类型,我对范围类型中的NULL值的含义有点困惑。文件上写着:
可以省略范围的下限,这意味着所有小于上限的值都包含在范围中,例如
(,3]
。同样,如果忽略范围的上限,则所有大于下限的值都包含在范围内。如果同时忽略了下限和上限,则元素类型的所有值都被认为在该范围内
这向我建议,一个范围中的省略边界(在范围类型的构造函数中指定的等效NULL边界)应该被视为无限。然而,PostgreSQL区分了NULL边界和无限边界。文件继续:
您可以将[范围]中的这些缺失值视为+/-无穷大,但它们是特殊的范围类型值,被认为超出了任何范围元素类型的+/-无穷大值。
这令人费解"超越无穷大";没有意义,因为无穷大值的整个点是没有可以大于+无穷大或小于-无穷大。这不会打破";"范围内的元素"-类型检查,但它确实为主键引入了一个有趣的情况,我想大多数人都不会想到。或者至少,我没有预料到。
假设我们创建一个基本表,其唯一字段是日期范围,它也是PK:
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)
的结果返回与X <@ (,2021-05-22)
不同的结果。这是有道理的,因为NULL值不能具有date
的类型,因此它们甚至不能与范围进行比较(PostgreSQL甚至将daterange(NULL,'2021-05-21','[]')
中NULL下界的包含边界转换为独占边界,(,2021-05-22)
是双重确定的)。但是,为什么在各个实际方面都相同的两个范围被认为是不同的呢?
当我还在上学的时候,我记得无意中听到一些关于";未知";以及";不存在"-两个比我聪明的人在讨论为什么NULL值经常会引起问题时谈到了这一点;未知";以及";不存在";价值观可能会解决这些问题,但当时讨论的内容超出了我的想象。想到这个奇怪的功能,我想到了那个讨论。";未知";以及";不存在";PostgreSQL为什么将NULL和+-infinity区别对待?如果是这样,为什么范围是PostgreSQL中唯一允许这种区别的类型?如果不是,为什么PostgreSQL将功能等价的值视为不同的值?
相反,我在问为什么PostgreSQL在NULL和无限边界之间进行区分,而(据我所知)它们的功能完全相同。
但他们没有。当用作范围的边界时,NULL
是一个语法便利,而-infinity
/infinity
是范围域中的实际值。抽象值意味着小于/大于任何其他值,但值(可以包括或排除)。
此外,NULL
适用于任何范围类型,而大多数数据类型没有像-infinity
/infinity
这样的特殊值。以integer
和int4range
为例。
为了更好地理解,请考虑a_mase提供的pgsql general中的线程:
- https://www.postgresql.org/message-id/flat/OrigoEmail.bf5.ac6ff6ffeb116aec.13fc29939e0%40prod2#c9fabdc670211364636b733a79a04712
这是有道理的,因为NULL值不能有日期类型,所以它们甚至不能与范围进行比较
每个数据类型都可以是NULL
,甚至可以是明确为NOT NULL
的域。参见:
- 为什么PostgreSQL在禁止NULL的域中允许NULL
当然包括date
(就像Adrian评论的那样):
test=> SELECT NULL::date, pg_typeof(NULL::date);
date | pg_typeof
------+-----------
| date
(1 row)
但是,试图将NULL
讨论为值(当用作范围的边界时)是一种误导性的方法。这不是一个价值。
。。。(PostgreSQL甚至将
daterange(NULL,'2021-05-21','[]')
中NULL下界的包含边界转换为互斥边界(,2021-05-22)
,这是双重肯定的)。
同样,NULL
不被视为范围域中的值。它只是作为一种方便的语法说:";无界";。仅此而已。