PostgreSQL: JSON
xsobi 2024-12-01 05:37 22 浏览
PostgreSQL支持非关系数据类型json (JavaScript Object Notation),本节介绍json类型、json与jsonb差异、json与jsonb操作符和函数,以及jsonb键值的追加、删除、更新。
JSON类型简介
PotgreSQL早在9.2版本已经提供了json类型,并且随着大版本的演进,PostgreSQL对json的支持趋于完善,例如提供更多的json函数和操作符方便应用开发,一个简单的json类型例子如下:
SELECT '{"a":1,"b":2}'::json;
json
---------------
{"a":1,"b":2}
创建一张表,如下所示:
CREATE TABLE test_json1 (id serial primary key,name json);
以上示例定义字段name为json类型,插入表数据,如下所示:
INSERT INTO test_json1 (name)
VALUES ('{"col1":1,"col2":"test","col3":"male"}');
INSERT 0 1
INSERT INTO test_json1 (name)
VALUES ('{"col1":2,"col2":"fp","col3":"female"}');
INSERT 0 1
查询表test_json1数据:
SELECT * FROM test_json1;
id | name
----+------------------------------------------
1 | {"col1":1,"col2":"francs","col3":"male"}
2 | {"col1":2,"col2":"fp","col3":"female"}
————————————————
查询JSON数据
通过 -> 操作符可以查询json数据的键值,如下所示:
SELECT name -> 'col2' FROM test_json1 WHERE id=1;
?column?
----------
"francs"
(1 row)
如果想以文本格式返回json字段键值可以使用->>符,如下所示:
SELECT name ->> 'col2' FROM test_json1 WHERE id=1;
?column?
----------
francs
(1 row)
JSONB与JSON差异
PostgreSQL支持两种JSON数据类型:json和jsonb,PostgreSQL 9.4 有加入了jsonb 类型,两种类型在使用上几乎完全相同,两者主要区别为以下:
- json存储格式为文本,而jsonb存储格式为二进制 ,由于存储格式的不同使得两种json数据类型的处理效率不一样,json类型以文本存储并且存储的内容和输入数据一样,当检索json数据时必须重新解析,而jsonb以二进制形式存储已解析好的数据,当检索jsonb数据时不需要重新解析,因此json写入比jsonb快,但检索比jsonb慢,后面会通过测试验证两者读写性能差异。
- json与jsonb在使用过程中还存在差异,jsonb输出的键的顺序和输入不一样,而json的输出键的顺序和输入完全一样,如下所示:
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb;
jsonb
--------------------------------------------------
{"bar": "baz", "active": false, "balance": 7.77}
(1 row)
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json;
json
-------------------------------------------------
{"bar": "baz", "balance": 7.77, "active":false}
(1 row)
- jsonb类型会去掉输入数据中键值的空格,而json的输出和输入一样,不会删掉空格键,如下所示:
CREATE TABLE books (
id SERIAL PRIMARY KEY,
client TEXT NOT NULL,
data JSONb NOT NULL
);
INSERT INTO books(client, data) values ( 'Joe',
'{ "title": "Siddhartha", "author": { "first_name": "Herman", "last_name": "Hesse" } }'
),( 'Jenny',
'{ "title": "Dharma Bums", "author": { "first_name": "Jack", "last_name": "Kerouac" } }'
),( 'Jenny',
'{ "title": "100 a?os de soledad", "author": { "first_name": "Gabo", "last_name": "Marquéz" } }'
);
SELECT * FROM books;
CREATE TABLE books_1 (
id SERIAL PRIMARY KEY,
client TEXT NOT NULL,
data JSONb NOT NULL
);
INSERT INTO books_1(client, data) values ( 'Joe',
'{ "title": "Siddhartha", "author": { "first_name": "Herman", "last_name": "Hesse" } }'
),( 'Jenny',
'{ "title": "Dharma Bums", "author": { "first_name": "Jack", "last_name": "Kerouac" } }'
),( 'Jenny',
'{ "title": "100 a?os de soledad", "author": { "first_name": "Gabo", "last_name": "Marquéz" } }'
);
SELECT * FROM books_1;
- jsonb会删除重复的键,仅保留最后一个,,而json数据类型会保留重复的键值,如下所示:
SELECT ' {"id":1,
"name":"francs",
"remark":"a good guy!",
"name":"test"
}'::jsonb;
jsonb
----------------------------------------------------
{"id": 1, "name": "test", "remark": "a good guy!"}
(1 row)
相比json大多数应用场景建议使用jsonb,除非有特殊的需求,比如对json的键顺序有特殊的要求。
JSONB与JSON操作符
PostgreSQL支持丰富的JSONB和JSON的操作符,举例如下:-> 以json对象形式返回, ->>以文本格式返回,如下所示:
select data -> 'title' as title from books;
select data -> 'author' ->> 'last_name' as last_name from books;
jsonb键/值的追加、删除、更新
jsonb键/值追加可通过||操作符,如下增加sex键/值:
select data || '{"sex":"male"}'::jsonb FROM books
jsonb键/值的删除有两种方法,一种是通过操作符号-删除,通过操作符号-删除键/值如下:
SELECT '{"name": "James", "email": "james@localhost"}'::jsonb
- 'email';
?column?
-------------------
{"name": "James"}
(1 row)
SELECT '["red","green","blue"]'::jsonb - 0;
?column?
-------------------
["green", "blue"]
第二种方法是通过操作符#-删除指定键/值,通常用于有嵌套json数据删除的场景,如下删除嵌套contact中的fax键/值:
SELECT '{"name": "James", "contact": {"phone": "01234 567890", "fax": "01987 543210"}}'::jsonb #- '{contact,fax}'::text[];
?column?
---------------------------------------------------------
{"name": "James", "contact": {"phone": "01234 567890"}}
(1 row)
删除嵌套aliases中的位置为1的键/值,如下所示:
SELECT '{"name": "James", "aliases": ["Jamie","The Jamester","J Man"]}'::jsonb #- '{aliases,1}'::text[];
?column?
--------------------------------------------------
{"name": "James", "aliases": ["Jamie", "J Man"]}
(1 row)
键/值的更新也有两种方式,第一种方式为||操作符,||操作符可以连接json键,也可覆盖重复的键值,如下修改age键的值:
SELECT '{"name":"francs","age":"31"}'::jsonb ||
'{"age":"32"}'::jsonb;
?column?
---------------------------------
{"age": "32", "name": "francs"}
(1 row)
第二种方式是通过jsonb_set函数,语法如下:
jsonb_set(target jsonb, path text[], new_value jsonb[, create_missing boolean])
target:指源jsonb数据,
path:指路径:
new_value:指更新后的键值;
create_missing: true表示如果键不存在则添加, false表示如果键不存在则不添加,默认值为true
示例如下:
SELECT jsonb_set('{"name":"francs","age":"31"}'::jsonb,'{age}','"32"'::jsonb,false);
jsonb_set
---------------------------------
{"age": "32", "name": "francs"}
(1 row)
SELECT jsonb_set('{"name":"francs","age":"31"}'::jsonb,'{sex}','"male"'::jsonb,true);
jsonb_set
------------------------------------------------
{"age": "31", "sex": "male", "name": "francs"}
(1 row)
给JSONB类型创建索引
给jsonb数据类型创建索引,jsonb数据类型支持GIN索引,为了便于说明,假如一个json字段内容如下,并且以jsonb格式存储。
{
"id": 1,
"user_id": 1440933,
"user_name": "1_francs",
"create_time": "2023-02-03 16:22:05.528432+08"
}
假如存储以上jsonb数据的字段名为user_info,表名为tbl_user_jsonb,在user_info字段上创建GIN索引语法如下:
CREATE INDEX idx_gin ON tbl_user_jsonb USING gin(user_info);
jsonb上的GIN索引支持@>、?、 ?&、?|操作符,例如以下查询将会使用索引。
SELECT * FROM tbl_user_jsonb WHERE user_info @> '{"user_name": "1_frans"}'
但是以下基于jsonb键值的查询不会走索引idx_gin,如下所示:
SELECT * FROM tbl_user_jsonb WHERE user_info->>'user_name'= '1_francs';
如果要想提升基于jsonb类型的键值检索效率,可以在jsonb数据类型对应的键值上创建索引,如下所示:
CREATE INDEX idx_gin_user_infob_user_name ON tbl_user_jsonb USING btree
((user_info ->> 'user_name'));
创建以上索引后,上述根据user_info->>'user_name'键值查询的SQL将会走索引。
JSON与JSONB读写性能测试
本小节将对json、jsonb读写性能进行简单对比,前面介绍json、jsonb数据类型时提到了两者读写性能的差异,主要表现为json写入时比jsonb快,但检索时比jsonb慢,主要原因为:json存储格式为文本而jsonb存储格式为二进制,存储格式的不同使得两种json数据类型的处理效率不一样,json类型存储的内容和输入数据一样,当检索json数据时必须重新解析,而jsonb以二进制形式存储已解析好的数据,当检索jsonb数据时不需要重新解析。
准备测试数据
下面通过一个简单的例子测试下json、jsonb的读写性能差异,计划创建以下三张表:
`user_ini:基础数据表,并插入200万测试数据;
tbl_user_json:: json 数据类型表,200万数据;
tbl_user_jsonb: jsonb 数据类型表,200万数据;
首先创建user_ini表并插入200万测试数据,如下:
CREATE TABLE user_ini(id int4 ,user_id int8, user_name character
varying(64),create_time timestamp(6) with time zone default
clock_timestamp());
INSERT INTO user_ini(id,user_id,user_name)
SELECT r,round(random()*2000000), r || '_francs'
FROM generate_series(1,2000000) as r;
计划使用user_ini表数据生成json、jsonb数据,创建user_ini_json、user_ini_jsonb表,如下所示:
CREATE TABLE tbl_user_json(id serial, user_info json);
CREATE TABLE tbl_user_jsonb(id serial, user_info jsonb);
JSON与JSONB表写性能测试
根据user_ini数据通过row_to_json函数向表user_ini_json插入200万json数据,如下:
INSERT INTO tbl_user_json(user_info) SELECT row_to_json(user_ini) FROM user_ini;
从以上结果看出tbl_user_json插入200万数据花了13秒左右;接着根据user_ini表数据生成200万jsonb数据并插入表tbl_user_jsonb,如下:
INSERT INTO tbl_user_jsonb(user_info) SELECT row_to_json(user_ini)::jsonb FROM user_ini;
从以上看出tbl_user_jsonb表插入200万jsonb数据花了20秒左右,正好验证了json数据写入比jsonb快。
比较两表占用空间大小,如下所示:
从占用空间来看,同样的数据量jsonb数据类型占用空间比json稍大。
查询测试
对于json、jsonb读性能测试我们选择基于json、jsonb键值查询的场景,例如,根据user_info字段的user_name键的值查询,如下所示:
上述SQL执行时间为186毫秒左右,基于user_info字段的user_name键值创建btree索引如下:
CREATE INDEX idx_jsonb ON tbl_user_jsonb USING btree ((user_info->>'user_name'));
再次执行上述查询,如下所示:
根据上述执行计划看出走了索引,并且SQL时间下降到0.147ms。为更好的对比tbl_user_json、tbl_user_jsonb表基于键值查询的效率,计划根据user_info字段id键进行范围扫描对比性能,创建索引如下:
CREATE INDEX idx_gin_user_info_id ON tbl_user_json USING btree (((user_info ->> 'id')::integer));
CREATE INDEX idx_gin_user_infob_id ON tbl_user_jsonb USING btree (((user_info ->> 'id')::integer));
根据以上看出,查询表tbl_user_json的user_info字段id键值在1到10000范围内的记录走了索引,并且执行时间为27毫秒,而tbl_user_jsonb的执行时间为13毫秒,从这个测试看出jsonb检索比json效率高。
从以上两个测试看出,正好验证了“json写入比jsonb快,但检索时比jsonb慢”的观点,值得一提的是如果需要通过key/value进行检索,例如以下。
SELECT * FROM tbl_user_jsonb WHERE user_info @> '{"user_name": "2_test"}';
这时执行计划为全表扫描,如下所示:
从以上看出执行时间为582毫秒左右,在tbl_user_jsonb字段user_info上创建gin索引,如下所示:
CREATE INDEX idx_tbl_user_jsonb_user_Info ON tbl_user_jsonb USING gin (user_Info);
索引创建后,再次执行以下,如下所示:
从以上看出走了索引,并且执行时间下降到了0.185毫秒。
JSONB与JSON函数
row_to_json()
能够将行作为json对象返回,此函数常用来生成json测试数据,比如将一个普通表转换成json类型表:
SELECT row_to_json(books) from books
json_each()
json与jsonb相关的函数非常丰富,举例如下: 扩展最外层的json对象成为一组键/值结果集,如下所示:
-- 键/值结果集
select jsonb_each(data) from books where id =1
-- 文本形式返
select jsonb_each_text(data) from books where id =1
json_object_keys()
返回最外层的json对像中的键的集合,如下所示:
update 待更新表 a
set 待更新表_字段一 = b.字段一 ,
待更新表_字段二 = b.字段二
from 关联表 b
where a.关联字段= b.关联字段
eg:
update test001 a
set name= b.name,age=b.age
from test002 b
where a.id= b.id
row_to_json()
将表的所有字段值以json的形式返回,如下所示:
但是有时候我们只需要查询指定的列,那么我们可以使用 row() 结构函数,如下所示:
虽然返回了指定列,但是缺遗失了字段名称,如需达到这样的效果,如下所示:
- 上一篇:postgresql新手入门
- 下一篇:PostgreSQL之慢SQL语句
相关推荐
- 5.5英寸触屏,搭载“安卓系统”的智能计算器评测:这设计挺脑洞
-
“计算器”可以说是我们日常生活中较为常用的一款电子产品,纵使手机上也有计算器功能,且足以替代实物计算器,但现在还是有很多人习惯用实物计算器,例如;做批发的店铺老板,计算器就放在店铺显眼位置,结账时顺手...
- Android之父晒新款手机,造型酷似遥控器
-
安迪·鲁宾大家可能并不陌生,鲁宾曾一手创建了安卓操作系统,被外界誉为“安卓之父”。2015年鲁宾又创立智能手机公司EssentialProductsInc,还获得亚马逊和腾讯的投资。在筹备两年后,...
- WP8.1的IE11为何不支持淘宝网触屏版?
-
IT之家(www.ithome.com):WP8.1的IE11为何不支持淘宝网触屏版?众所周知IE浏览器有自己的一个内核(简称IE内核),WP8.1系统的自带移动版IE11浏览器,但为什么iOS、An...
- 手机屏幕失灵乱跳乱点,屏幕时好时坏是怎么回事?
-
我们平时在使用手机的时候,如果我们的手机经常出现屏幕不受控制,手机屏幕会出现乱跳自己乱点的一些情况,这是什么问题呢?出现这种问题我们应该怎么去解决呢,今天我们九一手机维修就来跟大家说说这个问题该怎样去...
- 跨界表演有风险,百事手机 P1 众筹宣告失败
-
大家还记得曾经轰动一时的百事手机P1吗?这款10月份曝光、11月份众筹的手机在京东众筹失败,已经退款。想要喝着百事可乐玩着百事手机的网友,赶紧该干嘛干嘛去吧。据悉,百事可乐P1采用铝合金...
- “傀儡”病毒感染超10万台手机
-
本报讯(记者孙奇茹)手机在没人操作的情况下,竟然自己亮屏、执行一些动作。这不是闹鬼,而是手机中毒了。日前,猎豹移动安全实验室发出警报,全球首个伪造模拟用户操作的安卓病毒被截获,并被命名为“傀儡(Go...
- Android事件分发机制
-
事件分发机制Android事件分发是指在Android系统中,当用户触摸屏幕或执行其他操作时,系统如何将这些事件传递给正确的视图或组件进行处理的过程。Android事件分发遵循一种称为"事件分...
- Android让视图像玻璃一样破裂
-
AndroidUILibs之BrokenView1.说明BrokenView让视图产生玻璃破裂的效果。注意:该库只能在API14以上的设备上运行2.配置在模块的build.gradle上面添加...
- 车载大屏爽翻了?英国研究机构:大尺寸触摸屏比酒驾更危险
-
大屏不仅蔓延到手机,汽车也不例外,得益于更加直观的人机交互体验,车载触控大屏逐渐成为越来越多车企的主流选择。然而最新的一项研究证明——触控大屏比酒驾、毒驾更危险。日前,英国一项道路安全研究报告指出,当...
- 安卓系统被曝严重安全漏洞 恶意程序竟能秘密拍照或录制音视频
-
央视网消息:据今日俄罗斯网站20号报道,以色列一家知名网络安全公司宣称发现谷歌、三星等制造商生产的安卓手机,系统存在严重安全漏洞,黑客能够在未经手机机主许可的情况下,操控安卓手机秘密拍摄照片、录制视...
- 央视曝光:安卓系统曝漏洞!有人可能正在用你的手机...
-
近日,谷歌、三星等制造商生产的安卓手机,被曝出系统存在严重安全漏洞。黑客能够在未经手机机主许可的情况下,操控安卓手机秘密拍摄照片、录制音视频并上传。点击下方,先看视频↓↓↓视频来源:央视新闻利用漏洞!...
- 安卓系统曝漏洞!有人可能正在用你的手机秘密拍照
-
近日,谷歌、三星等制造商生产的安卓手机,被曝出系统存在严重安全漏洞。黑客能够在未经手机机主许可的情况下,操控安卓手机秘密拍摄照片、录制音视频并上传。利用漏洞“恶意应用程序”拍照、录音视频并上传据今日俄...
- 安卓用户注意了 你的爱机可能会面临新式恶意攻击
-
据外媒报道,最近,东京早稻田大学的三名学者称,现代Android智能手机容易受到一种名为“Tap'nGhost”的新型攻击,这种攻击可以使不存在的“手指”点击手机屏幕,以采取行动。“Tap'nGh...
- Android学习之Touch事件的处理
-
在移动开发过程当中,我们经常会遇到手势处理和事件触摸的情况,如果不了解整个事件的处理机制,对于开发的同学和码农是非常痛苦的,但是事件触摸的处理确实是一个非常复杂的过程,细讲起来,估计我都能讲迷糊,这里...
- 办公小技巧:制作模糊查询效果Excel下拉菜单
-
在Excel单元格中输入特定范围的内容时,一般会利用数据有效性生成下拉列表的方式进行,但如果源列表的内容太多(比方说有数百个),这时再通过下拉列表查找需要的值就很不方便了。如果采用智能感知的模糊查询下...
- 一周热门
- 最近发表
- 标签列表
-
- grid 设置 (58)
- 移位运算 (48)
- not specified (45)
- 导航栏 (58)
- context xml (46)
- scroll (43)
- dedecms模版 (53)
- c 视频教程下载 (33)
- listview排序 (33)
- characterencodingfilter (33)
- getmonth (34)
- label换行 (33)
- android studio 3 0 (34)
- html转js (35)
- 索引的作用 (33)
- checkedlistbox (34)
- xmlhttp (35)
- mysql更改密码 (34)
- 权限777 (33)
- htmlposition (33)
- 学校网站模板 (34)
- textarea换行 (34)
- 轮播 (34)
- asp net三层架构 (38)
- bash (34)