查询具有不同数据类型的 PostgreSQL JSONB 列

Querying PostgreSQL JSONB column with varying types for data

作为我的 PostgreSQL 数据库中模式的一部分,目前 运行ning 版本 11,但如果它解除阻塞则愿意升级:我有一个 jsonb 列 data,其中包含各种嵌套对象跨行的结构,我无法控制。例如:

第 1 行可能是:{'rootProperty': { 'nestedProperty': 'someStrVal' }}

第 2 行可能具有类似的架构:{'rootProperty': { 'nestedProperty': 2, 'otherNestedProperty': 'someOtherString' }}

当我尝试根据 jsonb 列中具有不同类型的 属性 查询 row/subset 行时,我遇到了困难。在此示例中,nestedProperty 是第 1 行中的字符串和第 2 行中的整数。

当我尝试 运行 查询

SELECT * FROM TABLE WHERE data -> 'rootProperty' ->> 'nestedProperty' = 'someStrVal'

事情 运行 很好,但如果我尝试

SELECT * FROM TABLE WHERE data -> 'rootProperty' ->> 'nestedProperty' > 1

SELECT * FROM TABLE WHERE (data -> 'rootProperty' ->> 'nestedProperty')::int > 1

查询错误,分别为 'operator does not exist: text > integer' 和 'invalid input syntax for integer: "someStrVal"'。

有没有一种方法可以使 jsonb 列具有可变模式,这些模式可能具有重叠的结构,尽管数据类型不同,但仍然可以查询所有这些模式?我不介意必须指定我要查找的类型,只要它可以跳过或绕过不符合该类型标准的行即可。

相同的 属性 具有不同类型的值开始时似乎很奇怪,但很可能您无法更改该“设计”。

Postgres 12 引入了对 SQL/JSON path 表达式的支持,它对数据类型转换很宽松,如果您尝试将 someStrVal 与数字进行比较,也不会出错。

查询:

select *
from the_table
where data @@ '$.rootProperty.nestedProperty > 1'

return 所有 nestedProperty 为有效数字的行都将大于 1。无法转换为数字的值将被静默忽略。

也可以写成:

where jsonb_path_exists(the_column, '$.rootProperty.nestedProperty ? (@ > 1)')

可以通过使用带有第三个参数的 jsonb_path_exists() 将值作为参数传递:

where jsonb_path_exists(the_column, '$.rootProperty.nestedProperty ? (@ > $nr)', '{"nr": 1}')

最后一个参数可以通过参数占位符传递,例如在 Java:

where jsonb_path_exists(the_column, '$.rootProperty.nestedProperty ? (@ > $nr)', cast(? as jsonb))

然后:

PreparedStatement pstmt = conn.prepareStatement(...);
pstmt.setString(1, "{\"nr\": 1}");

我不记得具体是在哪个版本中引入的,但是您可以使用 json_typeof 函数和 CASE 表达式将 属性 的值转换为正确的类型.我会使用 SQL 函数来保持我的查询整洁干净:

CREATE FUNCTION jsonb_to_integer(jsonb, text) RETURNS integer AS
$$
    SELECT CASE jsonb_typeof(->) 
           WHEN 'number' THEN (->>)::integer 
           ELSE null 
           END 
$$
LANGUAGE SQL
STABLE
RETURNS NULL ON NULL INPUT;

然后就是:

SELECT * FROM TABLE WHERE jsonb_to_integer(data, 'rootProperty') > 1;