为什么在 JSONB 中提取显式"null"作为文本会产生 SQL"null"?



我试图理解PostgreSQLjsonb类型中的处理null。 以

# select 'null'::jsonb is null;
?column? 
----------
f
(1 row)

我假设它们与 SQLnulls 不同(这是有道理的( - 根据手册,

SQL NULL 是一个不同的概念。

因此,这两个查询一点也不奇怪:

# select '{"a": 1, "b": null}'::jsonb->'b' is null;
?column? 
----------
f
(1 row)
# select '{"a": 1, "b": null}'::jsonb->'c' is null;
?column? 
----------
t
(1 row)

根据手册:

如果 JSON 输入没有匹配请求的正确结构,则字段/元素/路径提取运算符返回 NULL,而不是失败;例如,如果不存在此类元素。

然而,令人惊讶的地方是这样的:

# select '{"a": 1, "b": null}'::jsonb->>'b' is null;
?column? 
----------
t
(1 row)
# select '{"a": 1, "b": null}'::jsonb->>'c' is null;
?column? 
----------
t
(1 row)

后者我可以理解 - 我们从提取中获得一个 SQLnull,并将null转换为text使其保持null- 我认为->>以这种方式工作,正如手册所说

字段/元素/路径提取运算符返回与其左侧输入相同的类型(json 或 jsonb(,但指定为返回文本的运算符除外,这些运算符将值强制为文本。

(顺便说一句,我找不到确认将 SQLnull转换为任何其他类型会在 PostgreSQL 中再次产生null- 它是明确写在某个地方的吗?

但前者对我来说是个谜。 提取应该让我jsonbnull,我认为投射到text应该给我'null'(即,一个说"null"的字符串(,就像

# select ('null'::jsonb)::text;
text 
------
null
(1 row)

但是它返回了一个正确的SQLnull

为什么会这样?

在某种程度上,这是实现者的意见问题;在JSON数据类型和SQL数据类型之间进行转换时,并不总是能够找到完美的对应关系,特别是因为SQL NLL是如此奇怪。

但是它是如何实现的有一定的逻辑的。

SELECT (JSONB '{"a": "null"}' -> 'a')::text,
(JSONB '{"a": null}' -> 'a')::text;
text  | text 
--------+------
"null" | null
(1 row)

强制转换为text始终会产生一个结果,当强制转换回原始类型时,将生成原始值。这是PostgreSQL中的一个设计原则。

因此,JSON 字符串"null"和 JSONnull将被强制转换为不同的字符串。

现在看看这个:

SELECT JSONB '{"a": "null"}' ->> 'a',
JSONB '{"a": null}' ->> 'a';
?column? | ?column? 
----------+----------
null     | 
(1 row)

在这里,与上面的cast不同,PostgreSQL试图找到SQL中JSON值的最接近的等价物。您不希望字符串"null"保留其双引号,因为这在 SQL 中会是一个完全不同的字符串,对吧?

但另一方面,如果"null"null在SQL中以相同的方式表示,那也会感觉不对,不是吗?

据我所知,JSONnull的意思是"不存在",这是 SQL NULL 的含义之一。此外,拥有值为null的 JSON 属性与省略该属性意味着大致相同,不是吗?

因此,虽然有争论的余地,但我认为它的实施方式背后有一些押韵和原因。

最新更新