我需要选择“类别”字段中至少有一个值的所有行。 “categories”字段的类型是 JSONB。
数据库中我的“类别”字段包含一个数字数组:
[20,23,24]
我正在尝试这个:
jsonb_exists_any( finishes.categories, ARRAY[20, 23] )
但我收到此错误:
ERROR: cannot cast type integer[] to json
LINE 5: AND jsonb_exists_any( categories, ARRAY[20, 23]::json )
如果我用字符串数组替换字段中的数据,效果很好。例如:
["20","23","24"]
然后:
jsonb_exists_any( finishes.categories, ARRAY['20', '23'] )
但这不是我想要达到的目标。
有什么提示吗?
jsonb_exists_any()
自 9.4 版本起在 Postgres 中实现。但它并不是面向用户的函数,只是为了实现 jsonb
运算符 ?|
,您更愿意使用它:
SELECT jsonb '[20,23,24]' ?| '{20, 23}'::text[]; -- false (!)
仍然无法按照您想要的方式工作,因为操作员(如函数)回答了问题,引用了手册:
文本数组中的任何字符串是否作为顶级键或数组元素存在?
注意术语“字符串”。 JSON 数组的元素是数字而不是字符串。考虑:
SELECT jsonb_typeof (jsonb '20') -- number
, jsonb_typeof (jsonb '"20"'); -- string
这就是将数组元素转换为字符串时的效果:
SELECT jsonb '["20", "23", "24"]' ?| '{20, 23}'::text[]; -- true
即使这个“有效”:
SELECT jsonb '[20, "23", 24, {"foo":"bar"}]' ?| '{20, 23}'; -- true
与 Postgres 数组不同,JSON 数组允许异构元素类型。
当运算符解析明确时,正确的操作数会自动强制为
text[]
。
相关:
据我所知(在撰写本文时 Postgres 17)仍然没有完全简单的方法来匹配给定数组的 any 值。
解决方案包括:
jsonb
“包含”运算符 @>
与 ANY
构造结合起来。@>
期望右侧有一个 jsonb
值,因此您无法传递普通数组(如 int[]
或 numeric[]
):
SELECT jsonb '[20,23,24]' @> ANY (ARRAY [jsonb '[22]', jsonb '[23]']);
或者:
SELECT jsonb '[20,23,24]' @> ANY ((ARRAY ['[22]', '[23]'])::jsonb[]);
或者使用最少的语法:
SELECT jsonb '[20,23,24]' @> ANY ('{[22],[23]}');
(当运算符解析明确且输入未键入时,会自动强制右操作数。)
从 Postgres 12 开始,您还可以使用 jsonpath 运算符
@?
:
SELECT jsonb '[20,23,24]' @? '$[*] ? (@ == 22 || @ == 23)';
同样,没有合适的“重叠”运算符来检查整个数组或值列表,因此您必须将其拼写出来。
Postgres 数组 提供所需的功能。虽然都是数字,但您可以转换并使用 数组重叠运算符
&&
:
SELECT ARRAY(SELECT jsonb_array_elements_text(jsonb '[20,23,24]')::int) && '{22,23}'::int[];
...这提供了一系列的选择。参见:
相关:
如果数组就是全部,只需使用一个普通的 Postgres 数组开始,没有 JSON 包装器,你就可以了:
SELECT '{20,23,24}'::int[] && '{22,23}'::int[];