从Postgres中的数组删除JSON对象时出错

问题描述

我有一个包含两列的Postgres表timeline

  • user_id(varchar)
  • 项目(json)

这是items json字段的结构:

[
  {
    itemId: "12345",text: "blah blah"
  },//more items with itemId and text
]

我需要删除所有items等于给定值的所有itemId。例如12345

我有这个有效的SQL:

UPDATE timeline 
SET items = items::jsonb - cast(( 
  SELECT position - 1 timeline,jsonb_array_elements(items::jsonb)
  WITH ORDINALITY arr(item_object,position) 
  WHERE item_object->>'itemId' = '12345') as int)

工作正常。只有当子查询没有返回任何项目时,即没有itemId等于'12345'的项目时,它才会失败。在这些情况下,我会收到此错误:

“ items”列中的空值违反了非空约束

我该如何解决?

解决方法

尝试一下:

update timeline 
set items=(select 
           json_agg(j) 
          from json_array_elements(items) j 
          where j->>'itemId' not in ( '12345')
          );

DEMO

,

问题在于,当将null传递给-运算符时,它将导致表达式的null。这不仅违反了您的not null约束,而且可能也未达到您的期望。

这是一种克服它的方法:

UPDATE timeline 
SET items = items::jsonb - coalesce(
   cast(( 
     SELECT position - 1 timeline,jsonb_array_elements(items::jsonb)
     WITH ORDINALITY arr(item_object,position) 
     WHERE item_object->>'itemId' = '12345') as int),99999999)

一种更正确的方法是使用以下类似的方法收集要删除的所有索引。 如果在单个userId: 12345行中可能有多个user_id,那么这将失败或弄乱您的items (我必须测试来查看哪个),但至少它仅更新具有12345记录的行。

WITH deletes AS (
  SELECT t.user_id,e.rn - 1 as position
    FROM timeline t
   CROSS JOIN LATERAL JSONB_ARRAY_ELEMENTS(t.items)
          WITH ORDINALITY as e(jobj,rn)
   WHERE e.jobj->>'itemId' = '12345'
)
UPDATE timeline 
   SET items = items - d.position
  FROM deletes d
 WHERE d.user_id = timeline.user_id;

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...