PostgreSQL 与 JSON

PostgreSQL从9.2版本开始支持JSON数据类型,并提供很多函数和操作符维护json数据。从9.4开始,又增加了JSONB类型。以前听过一个故事,就是PG的工程师看了MongoDB的源码,觉得BSON的实现太渣,忍受不了,于是在PG上面实现了NoSQL。
很多同行已经把PG的NoSQL跟MongoDB作了对比??雌鹄?,确实没必要再用MongoDB了。https://blog.csdn.net/qq_36178899/article/details/81475739

JSON, JSONB 区别

json和jsonb,而两者唯一的区别在于效率,json是对输入的完整拷贝,使用时再去解析,所以它会保留输入的空格,重复键以及顺序等。而jsonb是解析输入后保存的二进制,它在解析时会删除不必要的空格和重复的键,顺序和输入可能也不相同。使用时不用再次解析。两者对重复键的处理都是保留最后一个键值对。效率的差别:json类型存储快,查询慢,jsonb类型存储稍慢,查询较快(支持许多额外的操作符)。(参考文章)

实验时间

下面我们创建一张新表用于说明jsonb数据类型:

CREATE TABLE user_ext (
   ID serial NOT NULL PRIMARY KEY,
   info jsonb NOT NULL
);

好!现在来Insert一些数据,并用SQL对JSON的Property进行查询。


insert into user_ext(info) values ('{"married": "yes", "education": "bachelor"}'),
('{"married": "no", "education": "master"}'),
('{"married": "no", "education": "master"}'),
('{"address": {"country": "China", "Province": "Guangdong"}}');

select * from user_ext where info->>'education'='master';

select * from user_ext where info->'address'->>'country'='China';

select * from user_ext where info->>'mobile'='master';

结果很完美!简单就上手,不需要像MongoDB那样,一周不碰就要查手册才会写Query。

select * from user_ext where info->>'education'='master';

| id | info|
| 2 | {"married": "no", "education": "master"} |
| 3 | {"married": "no", "education": "master"} |
| 6 | {"married": "no", "education": "master"} |
| 7 | {"married": "no", "education": "master"} |

(4 rows, 1 ms)

select * from user_ext where info->'address'->>'country'='China';

| id | info |
| 4 | {"address": {"country": "China", "Province": "Guangdong"}} |
| 8 | {"address": {"country": "China", "Province": "Guangdong"}} |

(2 rows, 1 ms)

select * from user_ext where info->>'mobile'='master';

| id | info |

(no rows, 1 ms)

PostgreSQL官网对json的一些说明

-- json和jsonb共有的运算符
-- 获取JSON数组元素(索引从0开始,从末尾开始计算负整数)
select '[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json->2 -- {"c":"baz"}
select '[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json->-1 -- {"c":"baz"}
-- 按KEY获取JSON对象字段
select '{"a": {"b":"foo"}}'::json->'a' -- {"b":"foo"}
-- 和->唯一的区别就是获取的值是文本
select '[1,2,3]'::json->>2 -- 3
select     '{"a":1,"b":2}'::json->>'b' -- 2
-- 可以深度的获取JSON对象
select     '{"a": {"b":{"c": "foo"}}}'::json#>'{a,b}' -- {"c": "foo"}
-- 可以深度的获取JSON对象作为文本
select     '{"a":[1,2,3],"b":[4,5,6]}'::json#>>'{a,2}' -- 3

-- jsonb特有的运算符
-- 左侧JSON值是否包含顶级正确的JSON路径,通俗点就是我们数学上说的包含关系
select '{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb -- true
select '{"a":1, "b":2}'::jsonb @> '{"b":4}'::jsonb -- false
select '{"a":1, "b":2}'::jsonb @> '{"c":4}'::jsonb -- false
select '{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb -- true
select '{"c":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb -- false
select '["a","b","c"]'::jsonb @> '["a"]' ::jsonb -- true
-- 这个字符串是否存在顶级键
select '{"a":1, "b":2}'::jsonb ? 'b' -- true
-- 这个字符串数组是否有一个是顶级键
select '{"a":1, "b":2, "c":3}'::jsonb ?| array['b', 'c'] -- true
select '{"a":1, "b":2, "c":3}'::jsonb ?| array['b', 'd'] -- false
-- 这个字符串数组是否都是顶级键
select '["a", "b"]'::jsonb ?& array['a', 'b'] -- true
select '["a", "b"]'::jsonb ?& array['a', 'c'] -- false
-- 将两个jsonb值连接成一个新的jsonb值
select '["a", "b"]'::jsonb || '["c", "d"]'::jsonb -- 时间过长 1.922s
-- 删除键值对
select '{"a":1, "b":2}'::jsonb - 'a'
select '{"a":1, "b":2}'::jsonb - 'b'
-- 删除具有指定索引的数组元素(从末尾开始计算负整数)。如果顶级容器不是数组,则会引发错误。
select '["a", "b"]'::jsonb - 1
-- 删除具有指定路径的字段或元素(对于JSON数组,从末尾开始计算负整数)
select '["a", {"b":1}]'::jsonb #- '{1,b}'

-- json创建函数
-- 返回值为json或jsonb
select to_json('Fred said "Hi."'::text) -- "Fred said \"Hi.\""
select to_jsonb('Fred said "Hi."'::text) -- "Fred said \"Hi.\""
-- 将数组作为JSON数组返回
select array_to_json('{{1,5},{99,100}}'::int[]) -- [[1,5],[99,100]]
select array_to_json('{{1,5},{99,100}}'::int[],true) -- [[1,5],[99,100]] 中间会有个换行符
-- 将行作为JSON对象返回,这个应该比较少用吧
select row_to_json(row(1,'foo')) -- {"f1":1,"f2":"foo"}
-- 从可变参数列表构建可能异构类型的JSON数组。
select json_build_array(1,2,'3',4,5)
select jsonb_build_array(1,2,'3',4,5) -- [1, 2, "3", 4, 5]
-- 从可变参数列表构建JSON对象。按照惯例,参数列表由交替的键和值组成。
select json_build_object('foo',1,'bar',2)
select jsonb_build_object('foo',1,'bar',2) -- {"bar": 2, "foo": 1}
-- 以下三种格式都可以转换成json对象
-- 从文本数组中构建JSON对象。数组必须具有一个具有偶数个成员的维度,在这种情况下,它们被视为交替的键/值对,或者两个维度,使得每个内部数组具有恰好两个元素,这些元素被视为键/值对。
select json_object('{a, 1, b, "def", c, 3.5}') -- {"a" : "1", "b" : "def", "c" : "3.5"}
select json_object('{{a, 1},{b, "def"},{c, 3.5}}') -- {"a" : "1", "b" : "def", "c" : "3.5"}
select json_object('{a, b}', '{1,2}') -- {"a" : "1", "b" : "2"}

-- JSON处理函数
-- 获取当前最外层json数组的长度
select json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]') -- 5
-- 将最外层的JSON对象扩展为一组键/值对。
select * from json_each('{"a":"foo", "b":"bar"}')
select * from json_each('{"a":"foo", "b":{"f1":1,"f2":[5,6]}}')
-- 和上面一样,只不过是text类型。
select * from json_each_text('{"a":"foo", "b":"bar"}')
-- 返回path_elems指向的JSON值(相当于#>运算符)。
select json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4') -- {"f5":99,"f6":"foo"}
select json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4','f5') -- 99
-- 返回path_elems指向的JSON值作为文本(相当于#>> 运算符)。
select json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4', 'f6') --foo
-- 返回最外层JSON对象中的键集。
select json_object_keys('{"f1":"abc","f2":{"f3":"a", "f4":"b"}}')
-- 以下两个官方例子无法使用
select * from json_populate_record(null::myrowtype, '{"a":1,"b":2}')
select * from json_populate_recordset(null::myrowtype, '[{"a":1,"b":2},{"a":3,"b":4}]')
-- 将JSON数组扩展为一组JSON值。
select * from json_array_elements('[1,true, [2,false]]')
select * from json_array_elements_text('["foo", "bar"]')
-- 以文本字符串形式返回最外层JSON值的类型
select json_typeof('-123.4') --number
select json_typeof('[1,true, [2,false]]') --array
select json_typeof('true') --boolean
-- 这些有点偏
select * from json_to_record('{"a":1,"b":[1,2,3],"c":"bar"}') as x(a int, b text, d text)
select * from json_to_recordset('[{"a":1,"b":"foo"},{"a":"2","c":"bar"}]') as x(a int, b text);
select json_strip_nulls('[{"f1":1,"f2":null},2,null,3]')
-- 这个专门替换的,昨天就是用这个,效果那叫一个好,默认最后是true,无则添加
select jsonb_set('[{"f1":1,"f2":null},2,null,3]', '{0,f1}','[2,3,4]', false)
select jsonb_set('[{"f1":1,"f2":null},2]', '{0,f3}','[2,3,4]')
-- 缩进的JSON文本返回
select jsonb_pretty('[{"f1":1,"f2":null},2,null,3]')
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容

  • 根据RFC 7159 中的说明,JSON 数据类型是用来存储 JSON(JavaScript Object Not...
    椟夜阅读 10,168评论 0 3
  • MongoDb与Postgres分析笔记 前言 postgres是我步入社会接触到的第一款关系型数据库,同时具有免...
    WANGGGGG阅读 2,385评论 0 0
  • Why 回顾了下自己所在项目中的数据存储技术,在关系数据库方面使用了 MySQL、PostgreSQL 这两种流行...
    张羽辰阅读 519评论 0 0
  • 一、MongoDB简介 1.概述 ? MongoDB是一个基于分布式文件存储的数据库,由C++语言编写。旨在为WE...
    郑元吉阅读 976评论 0 2
  • Mongodb数据库 课程介绍 ? 数据库概述(了解) ? NoSQL和MongoDB简介(了解) ? Mongo...
    致自己_cb38阅读 1,755评论 0 4