【问题标题】:Querying PostgreSQL JSONB column with varying types for data使用不同类型的数据查询 PostgreSQL JSONB 列
【发布时间】:2021-02-12 06:04:47
【问题描述】:

作为我的 PostgreSQL DB 中架构的一部分,当前运行版本 11,但如果它解除阻塞,我愿意升级:我有一个 jsonb 列 data,其中包含跨行的各种结构的嵌套对象,我不这样做'无法控制。例如:

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

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

我的困难在于尝试根据 jsonb 列中的属性查询行/行子集,该属性跨行具有不同的类型。在此示例中,nestedProperty 是第 1 行中的字符串和第 2 行中的 int。

当我尝试运行诸如

之类的查询时

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

查询出错,分别出现“运算符不存在:文本 > 整数”和“整数的无效输入语法:“someStrVal””。

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

【问题讨论】:

  • 这取决于你的 PostgreSQL 版本
  • @LaurenzAlbe 服务器当前正在运行 PostgreSQL 11。但如果它启用,我愿意更新到 12 或 13。将更新帖子以反映

标签: postgresql jsonb querying


【解决方案1】:

对同一个属性使用不同类型的值一开始似乎很奇怪,但很可能您无法更改这种“设计”。

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

查询:

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

将返回 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}");

【讨论】:

  • 啊,这太完美了,我将不得不考虑将我们的服务器更新到 Postgres 12。谢谢!
  • @Norrec:如果你升级,那么直接去最新版本(13)。没必要去 12
  • 如果您计划在获取外部数据的应用程序中使用此方法,通过任何特定于语言的驱动程序(psycopg2、Npgsql 等)发送 SQL 在构建表达式时要格外小心,因为您可以'不要在那个位置使用绑定变量,除非你自己引用,否则你很容易受到 SQL 注入的影响。
  • @fog:有一种方法可以通过例如PreparedStatement - 但它不是很漂亮;)
  • @a_horse_with_no_name 真的吗?你能给我一些指示吗?在某些情况下,在文本中绑定变量的能力真的很有帮助。
【解决方案2】:

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

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

然后就是:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-11
    • 1970-01-01
    • 2018-05-31
    • 2016-08-01
    • 2015-10-15
    • 2021-05-07
    • 1970-01-01
    • 2020-10-19
    相关资源
    最近更新 更多