为什么 PostgreSQL 在禁止 NULL 的域中允许 NULL?

Eva*_*oll 10 postgresql null domain

PostgreSQL 允许NULL在标记为 的域中使用s NOT NULL。这是为什么,文档是这么说的,

尽管存在这样的约束,但名义上属于域类型的列仍有可能读取为空。例如,如果域列位于外连接的可为空一侧,则这可能发生在外连接查询中。

这能更好地解释吗?

Eva*_*oll 13

嗯,首先看看这个,

CREATE DOMAIN mydomain AS int;

CREATE TABLE foo(bar) AS SELECT 42::mydomain;

SELECT f1.bar AS f1, f2.bar AS f2, pg_typeof(f1.bar), pg_typeof(f2.bar)
FROM foo AS f1
LEFT JOIN foo AS f2
  ON false;
Run Code Online (Sandbox Code Playgroud)

目前返回,

 f1 | f2 | pg_typeof | pg_typeof 
----+----+-----------+-----------
 42 |    | mydomain  | mydomain
Run Code Online (Sandbox Code Playgroud)

但它应该返回什么?您正在连接两个表,该表中唯一的类型是mydomain允许NULLs。从逻辑上讲,返回的两列都应该是类型mydomain。现在如果你回去运行这个,

CREATE DOMAIN mydomain AS int NOT NULL;
Run Code Online (Sandbox Code Playgroud)

考虑到上述命令,它是否应该返回不同的东西?答案是否定的——出于同样的原因。结果集由来自表的两列组成。如果要返回与源表相同的类型,如上面的查询,那么它必须返回一个带有NULL. 请注意,这不是关于INSERT它只是向您展示无论 SQL 的类型是什么都可以使其 return NULL。这就是你如何得到NULLs转换为DOMAIN当域本身不支持NULL

可能的解决方案

四值逻辑

你怎么能以正确的方式解决这个问题?您可以从Three-valued_logic (3vl)(真/假/空)升级到 4vl(类似于真/假/空/未知)。这将使SQL原始未提供给用户的是一切将在参数。它看起来像,

// Definitions                              // Value Constructors on 

// Two-value
type Bool = True | False;                   // True | False

// Three-value
type NonNullable<T> = T;                    // True | False
type Nullable<T> = Null | NonNullablle<T>;  // Null | True | False

// Four-value!
type Option<T> = Unknown | Nullable<T>;     // So over every column 
type Option<T> = Unknown | NonNullable<T>;  // you can store Unknown
Run Code Online (Sandbox Code Playgroud)

然后,SQL 作为 a 的结果返回的每一列SELECT 都可以Unknown用户无法选择的值,并且每种类型都必须允许。这样就可以解决这个问题了。它还会使 SQL 更难教和理解。想象一个四值真值表。

致命的 SELECT

来自Paul White 的另一种解决方案是,SELECT如果值不能被域强制或表示,则使语句抛出致命错误。一些SELECT语句已经抛出错误,例如SELECT 1/0SELECT CAST('foo' AS int);