我试图理解PostgreSQLjsonb
类型中的处理null
。 以
# select 'null'::jsonb is null;
?column?
----------
f
(1 row)
我假设它们与 SQLnull
s 不同(这是有道理的( - 根据手册,
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
- 它是明确写在某个地方的吗?
但前者对我来说是个谜。 提取应该让我jsonb
null
,我认为投射到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 属性与省略该属性意味着大致相同,不是吗?
因此,虽然有争论的余地,但我认为它的实施方式背后有一些押韵和原因。