PostgreSQL 对 JSON 的支持不仅限于存储数据。实际开发中,jsonb 更常用,因为它会以二进制格式存储 JSON,并提供结构化查询、修改和索引能力。
对于字段结构存在一定变化、但又希望直接利用数据库查询能力的业务数据,jsonb 是比较合适的方案。
常用操作符
PostgreSQL 提供了一组专门用于 JSON 和 JSONB 的操作符,可以完成字段提取、路径访问、结构匹配和键存在判断。
| 操作符 | 作用 | 示例 | ||
|---|---|---|---|---|
-> |
获取 JSON 对象或数组元素,结果仍为 JSON | data->'user' |
||
->> |
获取元素并转换为文本 | data->'user'->>'name' |
||
#> |
按路径获取 JSON | data#>'{user,address}' |
||
#>> |
按路径获取文本 | data#>>'{user,address,city}' |
||
@> |
判断左侧是否包含右侧 JSON 结构 | data @> '{"status":"paid"}' |
||
<@ |
判断左侧是否被右侧 JSON 包含 | data <@ '{"status":"paid"}' |
||
? |
判断对象是否存在指定键 | data ? 'status' |
||
? | |
判断是否存在任意一个指定键 | data ? | array['status','type'] |
||
?& |
判断是否同时存在全部指定键 | data ?& array['status','type'] |
其中,-> 和 ->> 是最常用的一组操作符:前者适合继续进行 JSON 操作,后者适合与普通字符串进行比较。@> 则是 JSONB 查询中非常重要的操作,适合判断对象是否包含指定字段和值。
JSONB 修改与拆分
PostgreSQL 可以直接修改 JSONB,而不需要先取出 JSON、在应用层修改后再完整写回。jsonb_set() 可以更新指定路径,- 可以删除顶层字段,#- 可以删除嵌套路径。例如可以使用 jsonb_set(data, '{user,name}', '"Tom"') 修改用户名称。
对于数组和对象,还可以使用 jsonb_array_elements()、jsonb_each() 等函数将 JSONB 拆分成多行,从而参与 JOIN、聚合和统计分析。这对于处理第三方接口数据、动态属性以及配置类数据尤其有用。
需要注意的是,JSONB 更新并不是简单的原地修改。大型 JSON 文档频繁更新时可能产生较明显的写放大,因此不适合把大量高频变化字段集中在一个 JSONB 文档中。
GIN 索引
如果业务大量使用 @>、? 等 JSONB 查询,可以建立 GIN 索引:
CREATE INDEX idx_data ON orders USING GIN (data);
GIN 默认使用 jsonb_ops,支持的操作范围较广;jsonb_path_ops 更适合以 @> 为主的包含查询,索引通常更紧凑,但支持的操作范围较小。因此索引选择应该根据实际查询模式决定,而不是看到 JSONB 就直接建立 GIN。
JSONB 的核心价值,是在关系型数据库中兼顾结构化查询能力与半结构化数据的灵活性。稳定且高频查询、排序或关联的字段,仍然应该设计为普通列;JSONB 更适合承载结构灵活、扩展性较强的数据。