如何在 Postgresql 中对 JSONB 数组中的值求和?

Posted

技术标签:

【中文标题】如何在 Postgresql 中对 JSONB 数组中的值求和?【英文标题】:How to sum a value in a JSONB array in Postgresql? 【发布时间】:2019-01-18 18:21:42 【问题描述】:

给定表ryzom_characters中的jsonb列p06中的以下数据:

        -[ RECORD 1 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    p06 | 
  "id": 675010,
  "cname": "Bob",
  "rpjobs": [
    
      "progress": 25
    ,
    
      "progress": 13
    ,
    
      "progress": 30
    
  ]

我正在尝试对 progress 的值求和。我尝试了以下方法:

    SELECT 
c.cname AS cname,
jsonb_array_elements(c.p06->'rpjobs')::jsonb->'progress' AS value 
FROM ryzom_characters c
Where cid = 675010
ORDER BY value DESC 
LIMIT 50;

正确列出了值:

 cname  | value
--------+-------
 Savisi | 30
 Savisi | 25
 Savisi | 13
(3 rows)

但现在我想对这些值求和,可能为空。

如何正确地对数组中的对象字段求和?

这是表结构:

                     Table "public.ryzom_characters"
    Column     |          Type          | Collation | Nullable | Default
---------------+------------------------+-----------+----------+---------
 cid           | bigint                 |           |          |
 cname         | character varying(255) |           | not null |
 p06           | jsonb                  |           |          |
 x01           | jsonb                  |           |          |

【问题讨论】:

数据结构不清楚。请编辑问题并添加表定义(psql 中的\d ryzom_characters)。 @klin 我添加了表结构。 【参考方案1】:

函数jsonb_array_elements() 是一个集合返回函数。因此,您应该将其用作行源(在FROM 子句中)。调用后,您有一个表,其中每一行都包含一个数组元素。从那里开始就相对容易了。

SELECT cname, 
       sum(coalesce(r.prog->>'progress'::int, 0)) AS value  
FROM ryzom_characters c,
     jsonb_array_elements(c.p06->'rpjobs') r (prog)
WHERE c.cid = 675010
GROUP BY cname 
ORDER BY value DESC 
LIMIT 50;

【讨论】:

这给出了错误:ERROR: COALESCE types jsonb and integer cannot be matched? 这是一个类型转换错误。查看更新的答案。 我收到错误:ERROR: invalid input syntax for integer: "progress"? 这表明progress 有一些不是整数的数据。【参考方案2】:

在 from 子句的横向连接中使用函数jsonb_array_elements()

select cname, sum(coalesce(value, '0')::int) as value
from (
    select 
        p06->>'cname' as cname, 
        value->>'progress' as value
    from ryzom_characters
    cross join jsonb_array_elements(p06->'rpjobs')
    where cid = 675010
    ) s
group by cname
order by value desc 
limit 50;

您可以使用左连接而不是交叉连接来保护查询免受不一致数据的影响:

    left join jsonb_array_elements(p06->'rpjobs')
    on jsonb_typeof(p06->'rpjobs') = 'array'
    where p06->'rpjobs' <> 'null'

【讨论】:

@kiln 是否可以通过该查询不受 cid 限制? cname 也是 'ryzom_characters' 表中的另一列,不是 p06 jsonb 中的元素,抱歉将更正描述。 当然可以跳过条件。然后,您可能希望(或不希望)将列添加到选择列表并在外部查询的group by 子句中使用它,具体取决于您的期望。 @kiln 我收到错误:无法从标量中提取元素。当我删除 where 子句时? 嗯,你的数据不一致,在某些行中'rpjobs' 不是数组。 @kiln 在某些行中它可能为空。是否可以再次保护它?

以上是关于如何在 Postgresql 中对 JSONB 数组中的值求和?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Postgresql 中实现对复杂嵌套 JSONB 的全文搜索

如何在 postgres 中对 jsonb 值求和?

如何使用PostgreSQL中的JSONB数据类型

如何逃脱? (问号)运算符在 Rails 中查询 Postgresql JSONB 类型

如何获取 PostgreSQL jsonb 字段的大小?

Postgresql:搜索jsonb对象数组时如何使用动态值?