问题描述
我有一个包含两列的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')
);
,
问题在于,当将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;