postgresql 的 #- 操作符用于安全删除 jsonb 中指定路径的键,接收 jsonb 值和 text[] 路径数组,路径必须精确匹配嵌套结构,不支持通配符,删除后空对象不自动收缩。

用 #- 操作符删除 JSONB 中指定路径的键
PostgreSQL 的 jsonb 类型原生支持按路径删除字段,核心就是 #- 操作符(注意是减号,不是连字符或破折号)。它接收一个 jsonb 值和一个文本路径数组(text[]),返回删除对应路径后的新 jsonb 值。
路径数组写法必须严格匹配嵌套结构:顶层键用单元素数组,如 {'a'};嵌套对象用多元素,如 {'c','d'} 表示 c.d;数组索引用字符串数字,如 {'items','0','name'}。
-
#-是「安全删除」:路径不存在时直接返回原值,不报错 - 只能删除「整个路径指向的值」,不能删掉中间某个键却保留其父对象空壳(比如删
c.d后c还在,哪怕只剩{}) - 不支持通配符或正则,路径必须精确指定
示例:从 {"a": "foo", "b": "BARK", "c": {"d": "dog"}} 中删掉 c.d:
UPDATE my_table SET data = data #- '{c,d}' WHERE id = 123;
批量删除多个路径要用多次 #- 链式调用
#- 每次只接受一个路径数组,没法一次传多个。想删多个字段,得连续用 #-,顺序无关,但每次都是对上一步结果操作。
常见错误是试图传二维数组或逗号分隔字符串——PostgreSQL 会直接报错 ERROR: operator does not exist: jsonb - text。
- 正确写法:
data #- '{a}' #- '{c,d}' #- '{items,1,flag}' - 错误写法:
data #- '{a,c,d}'(这会被当成一个三级路径a.c.d) - 错误写法:
data #- ARRAY['a','c,d'](类型不匹配)
如果路径来自变量或子查询,建议用 COALESCE 包一层防止 NULL 导致整条表达式为 NULL:
UPDATE my_table SET data = COALESCE(data #- '{tmp}', data) WHERE ...;
删除后空对象不会自动收缩,需手动清理
#- 只负责移除目标路径,不管父结构是否变空。比如删掉 c.d 后 c 变成 {},它依然存在;删掉 a 后顶层可能只剩 {},但字段值仍是合法 jsonb。
若业务要求「删净空壳」,得额外用 jsonb_strip_nulls() 或条件逻辑判断。但注意:jsonb_strip_nulls() 只删 null 值,不删空对象/空数组。
- 空对象
{}和空数组[]不会被jsonb_strip_nulls()处理 - 真要收缩,得用
jsonb_set()+jsonb_typeof()手动检测并替换,代价高,慎用 - 多数场景留着空对象不影响查询,没必要强求“物理干净”
WHERE 条件中用路径判断再删除,避免误删
直接 UPDATE ... SET data = data #- '{x}' 是无条件删除,风险高。更稳妥的是先用路径操作符查出符合条件的记录,再删。
常用判断方式:
- 检查键是否存在:
data ? 'a' - 检查嵌套键是否存在:
data @> '{"c":{"d":null}}'(注意null占位) - 检查具体值:
(data #>> '{c,d}') = 'dog' - 检查是否为对象类型:
jsonb_typeof(data -> 'c') = 'object'
组合使用可精准控制范围,例如只删 c.d 值为 "cat" 且 c 是对象的记录:
UPDATE my_table SET data = data #- '{c,d}'
WHERE data ? 'c'
AND jsonb_typeof(data -> 'c') = 'object'
AND (data #>> '{c,d}') = 'cat';
路径操作符的语义和性能都依赖底层二进制解析,只要路径写对、类型匹配,就基本不会踩到隐式转换或编码陷阱——但一旦路径写错(比如大小写、下划线漏掉),就会静默失效,这点最容易被忽略。











