我有一个嵌套很深的json数组。数组包含一个json对象列表,我想删除任何基于对象内字段匹配的对象。
{
"name": "John smith",
"items": {
"unknown-key-1": {
"file-array": [
{
"file-id": "file-1"
},
{
"file-id": "file-2"
},
{
"file-id": "file-3"
}
]
},
"unknown-key-2": {
"file-array": [
{
"file-id": "file-1"
},
{
"file-id": "file-2"
}
]
}
}
}
字符串
例如,假设我想删除所有file-id
为“file-1”的对象。我的UPDATE语句将删除所有与file-id
匹配的jsonb对象。在UPDATE之后,我的jsonb列将如下所示:
{
"name": "John smith",
"items": {
"unknown-key-1": {
"file-array": [
{
"file-id": "file-2"
},
{
"file-id": "file-3"
}
]
},
"unknown-key-2": {
"file-array": [
{
"file-id": "file-2"
}
]
}
}
}
型
当数组位于json的顶层时,或者当数组嵌套在对象中并且键已知时,我可以实现这一点。但在这种情况下,键是动态生成的(即“unknown-key-1”,“unknown-key-2”)
我知道在适当的情况下,我会将数据规范化,因为这是一个反模式,但是我别无选择。而且我想使用UPDATE语句而不是Postgres函数来实现这一点。
2条答案
按热度按时间nfeuvbwi1#
修改
jsonb
并不像应用它作为更新那样棘手:字符串
对于每一行,它列出了它的
items
,对于每一行,它列出了file-array
下的内容。这产生了可以在where
中过滤的项目名称和文件索引,并用于使用#-
减去路径。从with ordinality
产生的索引是基于1的,而jsonb
数组是基于0的,因此是-1
。直接将其用作更新批处理的问题是,它会导致基于每个文件的单个、单独的原子更改,这些更改将从每个值中删除,并且每行仅应用其中一个小更改。在您的示例中,只有
unknown-key-2
* 或 *unknown-key-1
下的文件会删除file-1
,而不是两者都删除。请参阅update
文档下的注解:当使用
FROM
时,您应该确保连接为每个要修改的行最多生成一个输出行。换句话说,目标行不应该连接到其他表中的多个行。如果是这样,则只有一个连接行将用于更新目标行,但将使用哪一个并不容易预测。您必须继续运行相同的更新,直到不再看到受影响的行,或者您需要压缩更新,以便每行只应用一个累积更改:demo at db<>fiddle
型
第一个CTE找到需要从哪一行删除的路径,第二个CTE不断迭代地应用这些更改,一个在另一个之上,第三个CTE
distinct on
只是将最新一轮的最终值传递给外部update
。WITH
需要是RECURSIVE
,第二个才能自引用,但只有那个才能自引用。More testsjljoyd4f2#
当你确切地知道你需要在整个JSON对象中查找哪些特定的键值,以及目标对象的一般结构和位置时,@ Zeberk的答案是有用的。然而,在一般情况下,你需要能够处理任何键值和任何对象结构和组合。下面的查询首先找到JSON中每个项目的路径,然后使用这些路径来搜索要删除的目标对象
字符串
See fiddle