简体   繁体   English

从 Postgres 的数组中删除 json object 时出错

[英]Error deleting json object from array in Postgres

I have a Postgres table timeline with two columns:我有一个包含两列的 Postgres 表timeline

  • user_id (varchar) user_id (varchar)
  • items (json)项目(json)

This is the structure of items json field:这是items json 字段的结构:

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

I need to delete all the items where itemId equals a given value.我需要删除itemId等于给定值的所有items eg 12345例如 12345

I have this working SQL:我有这个工作 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)

It works fine.它工作正常。 It only fails when no items are returned by the subquery ie when there are no items whose itemId equals '12345'.它仅在子查询没有返回任何项目时失败,即当没有itemId等于“12345”的项目时。 In those cases, I get this error:在这些情况下,我收到此错误:

null value in column "items" violates not-null constraint “项目”列中的 null 值违反非空约束

How could I solve this?我怎么能解决这个问题?

Try this:尝试这个:

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

DEMO 演示

The problem is that when null is passed to the - operator, it results in null for the expression.问题是当null传递给-运算符时,它会导致表达式为null That not only violates your not null constraint, but it is probably also not what you are expecting.这不仅违反了您的not null约束,而且可能也不是您所期望的。

This is a hack way of getting past it:这是一种克服它的黑客方式:

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)

A more correct way to do it would be to collect all of the indexes you want to delete with something like the below.一个更正确的方法是收集您想要删除的所有索引,如下所示。 If there is the possibility of more than one userId: 12345 within a single user_id row, then this will either fail or mess up your items (I have to test to see which), but at least it updates only rows with the 12345 records.如果在单个user_id行中有多个userId: 12345的可能性,那么这将失败或弄乱您的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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM