首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >JSONB操作:PG里玩转半结构化数据,查询更新真方便

JSONB操作:PG里玩转半结构化数据,查询更新真方便

作者头像
IT咸鱼
发布2025-08-08 13:23:14
发布2025-08-08 13:23:14
11600
代码可运行
举报
运行总次数:0
代码可运行

每天分享技术栈,开发工具等

不知道大家在工作中遇没遇到过这种蛋疼的情况:要做一个商品表,手机、衣服、家电啥都卖。手机有“屏幕尺寸”、“CPU型号”,衣服有“尺码”、“材质”,家电有“功率”、“能效等级”。。。我的天,这表的字段得设计多少个?难道来个新品类就得ALTER TABLE ADD COLUMN?那也太不灵活了,后期维护起来想死的心都有。

别慌,今天咱们就来聊聊PG里的大杀器——JSONB,专门治这种“字段不固定”的疑难杂症。它就像给你数据表里开了一个“万能口袋”,啥格式化的属性都能往里塞,而且查起来还贼快!

JSON 和 JSONB,傻傻分不清楚?

在开干之前,咱们得先分清两个“双胞胎”兄弟:JSONJSONB

PG里同时支持这两种类型,但咱们“IT咸鱼”要讲究,肯定得用那个更好的,对吧?

  • JSON类型:它就是个“文本存储器”。你存进去啥样,它就原封不动地存下来,包括空格、顺序啥的都保留。缺点是,每次查询的时候,PG都得重新解析一遍这个文本,性能上有点拉胯。
  • JSONB类型:这个B是Binary(二进制)的意思。你存数据的时候,PG会把它解析成一种优化的二进制格式。它不保留多余的空格和key的顺序,但换来的是极快的处理速度和强大的索引支持

一句话总结:如果你不需要保留JSON原始文本的每个字节(比如空格),只想高效地存取和查询数据,无脑用JSONB就对了!在咱们99%的场景里,JSONB都是最优选。

开干!创建一张“万能”商品表

光说不练假把式。打开你的数据库连接工具(我这里用的是DataGrip,强烈推荐,谁用谁知道),咱们连上之前部署好的PG数据库,准备动手。

我假设你用的PG版本是比较新的,比如PostgreSQL 16,我这边也是。不过别担心,JSONB的核心功能在PG 9.4之后的版本里就很成熟了,大部分操作都是通用的。你可以在DataGrip的查询控制台里执行 SELECT version();看看你的版本。

代码语言:javascript
代码运行次数:0
运行
复制

-- 在DataGrip的查询控制台或psql命令行里执行
SELECT version();

这个命令会返回类似这样的信息,让你对自己的环境心中有数 。 PostgreSQL 16.0 (Debian 16.0-1.pgdg120+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit

好,现在咱们来创建一个商品表products。它有几个固定字段,比如自增ID、商品名、创建时间,然后最重要的,来一个JSONB类型的字段叫attrs(attributes的缩写),用来装那些千变万化的商品属性。

代码语言:javascript
代码运行次数:0
运行
复制

-- 在DataGrip的查询控制台执行
-- 创建一个商品表
CREATETABLE products (
    id SERIALPRIMARYKEY,-- 自增ID,主键
    name VARCHAR(255)NOTNULL,-- 商品名,不能为空
    attrs JSONB,-- 我们的主角:JSONB类型的“万能口袋”
    created_at TIMESTAMPTZ DEFAULTNOW()-- 创建时间,带时区,默认为当前时间
);

-- 给小白的注释:
-- SERIAL: 这是PG特有的,会自动创建一个自增的整数序列,非常适合做主键。
-- PRIMARY KEY: 声明id是主键,保证唯一且非空。
-- VARCHAR(255): 存最多255个字符的字符串。
-- TIMESTAMPTZ: 带时区的时间戳,可以避免时区混乱问题。

表建好了,咱们来插几条数据进去,模拟一下真实的商品信息 。

代码语言:javascript
代码运行次数:0
运行
复制

-- 在DataGrip的查询控制台执行
-- 插入一条手机数据
INSERTINTO products (name, attrs)VALUES
('SuperPhone Pro Max','{
    "brand": "IT咸鱼",
    "price": 8999.00,
    "in_stock": true,
    "tags": ["旗舰", "新品", "5G"],
    "specs": {
        "screen": "6.7 inch OLED",
        "cpu": "S1 Chip",
        "storage": "256GB"
    }
}');

-- 插入一条T恤数据
INSERTINTO products (name, attrs)VALUES
('简约印花T恤','{
    "brand": "咸鱼优品",
    "price": 99.00,
    "in_stock": true,
    "tags": ["纯棉", "夏季"],
    "specs": {
        "color": "白色",
        "size": "L"
    }
}');

-- 插入一条缺货的笔记本电脑数据
INSERTINTO products (name, attrs)VALUES
('GamerBook X1','{
    "brand": "IT咸鱼",
    "price": 12999.00,
    "in_stock": false,
    "tags": ["游戏本", "RTX"],
    "specs": {
        "screen": "16 inch 165Hz",
        "cpu": "Core i9",
        "storage": "1TB SSD"
    }
}');

看,是不是很灵活?手机和T恤的specs结构完全不同,但都能轻松存到attrs这个“口袋”里。

JSONB查询操作符:指哪打哪

存进去只是第一步,能方便地查出来才是王道。JSONB有一套超级好用的操作符,咱们捡几个最常用的来盘一盘。

1. ->->>:取单层数据

这是最基础的操作符,用来获取JSON对象里的某个key对应的值。

  • ->:返回的结果是JSON类型
  • ->>:返回的结果是TEXT类型

啥意思呢?看例子就懂了。

代码语言:javascript
代码运行次数:0
运行
复制

-- 在DataGrip的查询控制台执行

-- 例子1:获取所有商品的品牌 (brand)
-- 使用 ->>,因为我们通常需要的是文本格式的品牌名
SELECT
    name,
    attrs ->>'brand'AS brand_name -- 使用 ->> 直接获取文本
FROM
    products;

DataGrip截图效果:

看,->>直接把品牌名给你干干净净地取出来了。那如果用->呢?

代码语言:javascript
代码运行次数:0
运行
复制

-- 在DataGrip的查询控制台执行

-- 对比一下 -> 和 ->> 的区别
SELECT
    name,
    attrs ->'price'AS price_json,-- 使用 -> 获取的是JSON数字
    attrs ->>'price'AS price_text -- 使用 ->> 获取的是文本
FROM
    products
WHERE
    name ='SuperPhone Pro Max';

你会看到,price_json列返回的是8999.00(一个JSON数字),而price_text列返回的是'8999.00'(一个字符串)。

记住这个关键区别:当你想把取出的值用在WHERE条件里和文本比较,或者直接显示给用户时,用->>;如果你想对取出的JSON值继续进行JSON操作,那就用->

2. #>#>>:深入嵌套,直捣黄龙

如果我想获取specs里面的cpu型号咋办?specs本身就是个嵌套的JSON对象。这时候就轮到路径操作符上场了。

  • #>:按路径获取,返回JSON类型
  • #>>:按路径获取,返回TEXT类型

路径用一个文本数组来表示,比如'{specs, cpu}'就代表先找specs,再在specs里找cpu

代码语言:javascript
代码运行次数:0
运行
复制

-- 在DataGrip的查询控制台执行

-- 获取SuperPhone的CPU型号
SELECT
    name,
    attrs #>> '{specs,cpu}' AS cpu_model -- 一步到位,获取嵌套的CPU型号
FROM
    products
WHERE
    name ='SuperPhone Pro Max';

这一下就能拿到 'S1 Chip',是不是很方便?

3. @>:终极杀器,包含查询

这个操作符简直是神技!它的意思是“包含”。可以判断一个JSONB值是否包含了另一个JSONB值。这在筛选数据时极其有用。

代码语言:javascript
代码运行次数:0
运行
复制

-- 在DataGrip的查询控制台执行

-- 例子1:查询所有品牌是“IT咸鱼”的商品
-- '{"brand": "IT咸鱼"}' 这个小JSON,是不是被 attrs 这个大JSON包含了?
SELECT name, attrs ->>'price'as price
FROM products
WHERE attrs @>'{"brand": "IT咸鱼"}';

-- 例子2:查询所有库存为 true 的商品
SELECT name, attrs ->>'price'as price
FROM products
WHERE attrs @>'{"in_stock": true}';

-- 例子3:查询标签(tags)里包含“新品”的商品
-- 注意,对于数组,它会判断右边的数组元素是否都在左边的数组里
SELECT name
FROM products
WHERE attrs ->'tags' @>'["新品"]';

@>操作符的强大之处在于,它可以深入到嵌套结构,也可以查询数组,几乎满足了我们日常80%的复杂查询需求。

为了让你感受一下PG官方文档的强大,下面这张图截取自官方文档页面,展示了部分JSON相关的函数和操作符。没事多去翻翻,宝藏很多!

更新JSONB数据:jsonb_set函数

查会了,那改呢?比如我想把“GamerBook X1”的价格改一下,或者给它加个“内存”属性。

这时候就要用到jsonb_set函数了。它的用法稍微复杂点,但逻辑很清晰:

jsonb_set(target_jsonb, path_array, new_value_jsonb, [create_if_missing])

  • target_jsonb: 你要操作的那个JSONB字段,比如attrs
  • path_array: 一个路径数组,告诉函数你要改哪个位置,比如'{price}'
  • new_value_jsonb: 你要设置的新值,必须是jsonb格式,比如'11999.00'::jsonb
  • create_if_missing: 一个可选的布尔值。如果是true,路径不存在时会自动创建;如果是false(默认),路径不存在就不做任何事。
代码语言:javascript
代码运行次数:0
运行
复制

-- 在DataGrip的查询控制台执行

-- 例子1:把GamerBook X1的价格从12999改成11999
UPDATE products
SET attrs = jsonb_set(attrs,'{price}','11999.00'::jsonb)
WHERE name ='GamerBook X1';

-- 例子2:给GamerBook X1增加一个内存(memory)属性
-- 因为原来没有memory,所以第四个参数要设为true
UPDATE products
SET attrs = jsonb_set(attrs,'{specs,memory}','"32GB DDR5"'::jsonb,true)
WHERE name ='GamerBook X1';

-- 检查一下修改结果
SELECT name, attrs FROM products WHERE name ='GamerBook X1';

执行完上面的语句,你会发现GamerBook X1的价格和属性都已经被成功更新了,非常精准!

性能起飞:别忘了GIN索引!

JSONB的查询很快,但当你的数据量达到几十万、几百万时,即便是二进制也扛不住全表扫描。想让你的@>查询快到飞起吗?你必须给它建一个GIN索引

代码语言:javascript
代码运行次数:0
运行
复制

-- 在DataGrip的查询控制台执行
CREATEINDEX idx_products_attrs_gin ON products USING GIN (attrs);

就这么简单一行代码,PG就会为attrs字段里的所有键值对创建一个高效的反向索引。之后,你再用@>去查询,PG就能通过索引秒级定位到数据,而不是傻傻地去扫描整张表 。

记住:只要你准备在JSONB字段上频繁使用@>??|?&这些操作符进行查询,一定要创建GIN索引!

怎么样,是不是感觉JSONB也没有那么神秘?它只是一个工具,一个能帮你优雅地解决“动态字段”问题的强大工具。作为一名立志成为优秀DBA的“IT咸鱼”,熟练掌握JSONB绝对能让你的技术栈更有深度。

今天的分享就到这里,赶紧打开你的DataGrip亲自敲一遍试试吧!实践出真知,我们下期再见!

代码语言:javascript
代码运行次数:0
运行
复制
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-08-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT咸鱼 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JSON 和 JSONB,傻傻分不清楚?
  • 开干!创建一张“万能”商品表
  • JSONB查询操作符:指哪打哪
    • 1. ->和 ->>:取单层数据
    • 2. #>和 #>>:深入嵌套,直捣黄龙
    • 3. @>:终极杀器,包含查询
  • 更新JSONB数据:jsonb_set函数
  • 性能起飞:别忘了GIN索引!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档