【问题标题】:Querying JSONB array value for sub values?查询子值的 JSONB 数组值?
【发布时间】:2020-05-22 18:50:24
【问题描述】:

我有一个 JSONB 对象:

{"name": "Foo", "interfaces": [{"name": "Bar", "status": "up"}]}

它存储在一个表的jsonb列中

create table device (
  device_name character varying not null,
  device_data jsonb not null
);

所以我试图通过接口未“启动”的设备的名称来计数。 Group By 用于按 naame 开发计数,但我在查询 json 列表中的值时遇到问题。

我的第一次尝试是:

select device_name, count(*) from device where device_json -> 'interfaces' -> 'status' != 'up' group by device_name;

让我觉得有些事情会变得困难的一些周围数据是:

select count(device_data -> 'interfaces') from device;

我认为这会让我计算所有设备的所有接口,但这是不正确的。它似乎只是从第一项返回计数。

我想我可能需要对内部内容进行子查询或连接。

我一直在考虑,在查找 psql 时,似乎我还没有找到一种方法来查询 jsonb 对象中的列表类型。也许我弄错了。我不想在此之上构建业务层,因为我认为 DBMS 将能够处理这种繁重的工作。

我看到有一个函数 jsonb_array_elements_text(device_data -> 'interfaces')::jsonb -> 'status' 可以返回信息,但我无法在其中进行任何计数,因为count(jsonb_array_elements_text(device_data -> 'interfaces')::jsonb -> 'status') 将返回错误:在不能接受集合的上下文中调用的集合值函数

【问题讨论】:

  • 如果您的数组包含多个接口名称怎么办?例如。两个上升,三个下降?
  • 我试图从本质上计算“向下”。因此,对于每一行/设备,我会知道有多少接口已关闭。
  • 因此,如果一台设备有两个向上和三个向下的接口,您希望该设备的计数为 3?
  • 你是对的。我不知道这样做是否会更困难:device_name、count(ALL INTERFACES)、count(DOWN INTERFACE based on STATUS property),但至少,你是正确的。

标签: sql json postgresql


【解决方案1】:

您需要横向连接来取消嵌套数组并计算向下(或未向上)的元素

select d.device_name, t.num_down
from device d
  cross join lateral (
     select count(*) num_down
     from jsonb_array_elements(d.device_data -> 'interfaces') as x(i)
     where i ->> 'status' = 'down'
  ) t

要统计所有接口和down接口,可以使用过滤聚合:

select d.device_name, t.*
from device d
  cross join lateral (
     select count(*) as all_interfaces,
            count(*) filter (where i ->> 'status' = 'down') as down_interfaces
     from jsonb_array_elements(d.device_data -> 'interfaces') as x(i)
  ) t

Online example

【讨论】:

  • 我在想加入和 unnest 可能是最好的做法。 DBMS 中是否有计时器,我想测试您的运行时间与 Bergi 的运行时间
  • @Fallenreaper:比较explain (analyze) select ...的结果。但是bergi 的第二个陈述本质上是在做同样的事情。
  • 出于好奇,如果 device_data 保存为空对象,用例是什么?访问接口属性时似乎是一个软错误,并且会像数组大小为 0 一样?
  • 它们确实非常相似。看起来我拥有的数据集的差异只有 6.5 毫秒。太棒了
  • @Fallenreaper:不确定您的意思,如果 JSON 为空,则不会有任何变化:dbfiddle.uk/…
【解决方案2】:

jsonb_array_elements 是正确的想法,我认为您正在寻找一个EXISTS 条件来匹配您的描述“设备接口不是'up'”:

SELECT device_name, count(*)
FROM device
WHERE EXISTS (SELECT *
  FROM jsonb_array_elements(device_json -> 'interfaces') interface
  WHERE interface ->> 'status' != 'up')
GROUP BY device_name;

我想知道有多少接口宕机了

这是一个不同的问题,为此您可以在SELECT 子句中使用子查询,并且可能不需要进行任何分组:

SELECT
  device_name,
  ( SELECT count(*)
    FROM jsonb_array_elements(device_json -> 'interfaces') interface
    WHERE interface ->> 'status' != 'up'
  ) AS down_count
FROM device

【讨论】:

  • 我的目标是基于每个设备。看起来exists命令将假设合理地辨别设备是否具有任何应该报告的非启动状态接口。我的目标是获取设备名称的总列表以及每个设备下未启动的接口数量。如果这有意义吗?的确。选择会返回至少有 1 个关闭界面的设备列表听起来也非常有用
  • 是否也有内置的 DBMS 来进行时间选择?现在():[选择]; now() 什么的?
猜你喜欢
  • 1970-01-01
  • 2015-11-28
  • 2018-06-16
  • 2019-07-02
  • 2022-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
相关资源
最近更新 更多