在示例表中:
CREATE TABLE example (
id SERIAL PRIMARY KEY,
data JSON NOT NULL );
INSERT INTO example (id, data) VALUES
(1, '[{"key": "1", "value": "val1"}, {"key": "2", "value": "val2"}]')
, (2, '[{"key": "1", "value": "val1"}]')
, (3, '[{"key": "1", "value": "val1"}, {"key": "2", "value": "val2"}]');
我想查询key = 2的数据列中的值字段。我当前使用的查询如下:
SELECT id,
jsonb_path_query(
TO_JSONB(data),
'$[*] ? (@.key == "2").value'::JSONPATH
)::VARCHAR AS values
FROM example
我希望结果是:
| 身份证|价值观|
| - ------|- ------|
| 1个|“val2”|
| 第二章|零|
| 三个|“val2”|
但实际结果是:
| 身份证|价值观|
| - ------|- ------|
| 1个|“val2”|
| 三个|“val2”|jsonb_path_query()
的空输出被省略是否有原因?
我如何让它按照我所期望的方式工作?
4条答案
按热度按时间ct3nt3jp1#
标量函数 * 处理 * 一个值,并且可以使它无效,但是一个返回集合的函数 * 生成 * 值,所以它可能最终根本不生成值。
或者,您可以保留SRF,即返回集合的函数
jsonb_path_query()
→ setof jsonb
,方法是将其 Package 在标量子查询中或替换其隐式内部联接。这两种方法都将使用空值指示缺少生成的值,但后者仍然可以派生并返回多个:demo第2行没有在该函数中生成任何行,这就是为什么您没有看到它的原因-没有可以包含
null
的行。选择部分不是SRF的正常位置-当应用到那里时,它最终被下推到from
部分,成为inner joined
。而inner join
具有空集,是空集。结合implicit join行为,这就是为什么您可以阅读:SELECT srf();
作为实际的SELECT b.x FROM srf() AS b(x);
SELECT a.id, srf() FROM a;
表示SELECT a.id, b.x FROM a, srf() AS b(x);
SELECT a.id, b.x FROM a, srf(a.v) AS b(x);
和SELECT a.id, srf(a.v) FROM a;
的实际含义是:SELECT a.id, b.x FROM a INNER JOIN srf(a.v) AS b(x) ON true;
这个
INNER JOIN
抛出了你的id
,它没有生成结果data
。LEFT JOIN
不会。另一个带有更明显的
generate_series()
“给予我这么多行”的插图SRF:demoyeotifhr2#
如果需要路径表达式的结果,则需要
jsonb_path_query_first()
:请注意,这将返回
jsonb
值。如果需要text
值,请用途:mzmfm0qo3#
根据PostgreSQL文档,过滤器充当
WHERE
条件定义路径时,还可以使用一个或多个筛选表达式,其工作方式类似于SQL中的WHERE子句。筛选表达式以问号开头,并在括号中提供条件:
我使用
LATERAL
和LEFT JOIN
成功实现了您所期望的目标结果
rbpvctlc4#
关于原问题:
...为什么省略
jsonb_path_query
的空输出?以及在tim-mccurrach的奖金中添加的问题:
...为什么在空结果的情况下从结果中省略整行。
两者都建立在一个微妙的错误假设上,即会有一个“空输出”或“空结果”。但是,一开始,实际上只有 “无行”。这就造成了所有的差异。相关:
jsonb_path_query()
是一个返回集合的函数(“SRF”)。它不返回1个结果,而是返回0-n个结果行。标准SQL不允许在
SELECT
列表中使用SRF,只允许在FROM
子句中使用SRF。但是Postgres(像其他一些RDBMS一样)允许使用SRF。传统上,这会导致奇怪的情况。最终,Postgres 10解决了确切的行为。请参见:返回集合的 single 函数,如示例所示:
实际上几乎1等于:
(隐式)连接消除了SRF为空(“no row”!)的输入行。
OTOH它在SRF产生多个结果行的地方乘以输入行。
手册中有一个示例在“返回集合的SQL函数”一章中准确地解释了这种情况:
在最后一个
SELECT
中,请注意,Child2
、Child3
等没有输出行出现。这是因为listchildren
为这些参数返回了一个空集,所以没有生成结果行。这与我们在使用LATERAL
语法时从函数结果的内部连接中获得的行为相同。PostgreSQL在查询的选择列表中的集合返回函数的行为几乎与在
LATERAL FROM
-子句项中编写的集合返回函数完全相同。几乎相当于
粗体强调是我的。
还有更多,包括上面“几乎“1的细节。阅读它。
溶液
直接使用
jsonb
来修剪投射json
时产生的噪波。最初的问题忽略了JSON数组中的多个对象都可以符合条件的事实。
要像原始查询一样获取**所有匹配项,并保留没有匹配项的输入行,请将隐式
CROSS JOIN
替换为LEFT JOIN ... ON true
。(然后“no row”将转换为null
!):参见:
仅获取第一个匹配,同时保留所有输入行:
或者获取所有匹配作为JSON数组,同时保留所有输入行:
fiddle-添加示例行,生成两个匹配项以传达要点。
正确转换为
text
我的两个查询都使用相同的“技巧”将JSON值转换为a_horse已经显示的
text
:#>> '{}'
。下面是一个出色的解释:JSON
#>>
操作符提取指定路径下的JSON子对象text
。该路径以Postgres文本数组text[]
的形式提供。'{}'
是一个空数组。因此,我们得到根对象text
。将JSON数组转换为
text[]
稍微复杂一些:这两个表达式都将JSON数组转换为Postgres
text[]
。第二个表达式将其封装在一个函数中,如下所示: