PostgreSQL不只是一个关系型数据库,同时它还支持非关系数据类型json (JavaScript Object Notation), json属于重量级的非常规数据类型,本节将介绍json类型、json与jsonb差异、json与jsonb操作符和函数,以及jsonb键值的追加、删除、更新。
json类型简介
PostgreSQL早在9.2版本已经提供了json类型,并且随着大版本的演进,PostgreSQL对json的支持趋于完善,例如提供更多的json函数和操作符方便应用开发,一个简单的json类型例子如下:
mydb=> SELECT '{"a":1, "b":2}'::json;
json
---------------
{"a":1, "b":2}
为了更好地演示json类型,接下来创建一张表,如下所示:
mydb=> CREATE TABLE test_json1 (id serial primary key, name json);
CREATE TABLE
以上示例定义字段name为json类型,插入表数据,如下所示:
mydb=> INSERT INTO test_json1 (name)
VALUES ('{"col1":1, "col2":"francs", "col3":"male"}');
INSERT 0 1
mydb=> INSERT INTO test_json1 (name)
VALUES ('{"col1":2, "col2":"fp", "col3":"female"}');
INSERT 0 1
查询表test_json1数据,如下所示:
mydb=> SELECT * FROM test_json1;
id | name
-------+------------------------------------------
1 | {"col1":1, "col2":"francs", "col3":"male"}
2 | {"col1":2, "col2":"fp", "col3":"female"}
查询json数据
通过“->”操作符可以查询json数据的键值,如下所示:
mydb=> SELECT name -> 'col2' FROM test_json1 WHERE id=1;
?column?
----------
"francs"
(1 row)
如果想以文本格式返回json字段键值可以使用“->>”操作符,如下所示:
mydb=> SELECT name ->> 'col2' FROM test_json1 WHERE id=1;
?column?
----------
francs
(1 row)
jsonb与json差异
PostgreSQL支持两种JSON数据类型:json和jsonb,两种类型在使用上几乎完全相同,两者主要区别为以下:json存储格式为文本而jsonb存储格式为二进制,由于存储格式的不同使得两种json数据类型的处理效率不一样,json类型以文本存储并且存储的内容和输入数据一样,当检索json数据时必须重新解析,而jsonb以二进制形式存储已解析好的数据,当检索jsonb数据时不需要重新解析,因此json写入比jsonb快,但检索比jsonb慢,后面会通过测试验证两者读写性能的差异。除了上述介绍的区别之外,json与jsonb在使用过程中还存在差异,例如jsonb输出的键的顺序和输入不一样,如下所示:
mydb=> SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb;
jsonb
--------------------------------------------------
{"bar": "baz", "active": false, "balance": 7.77}
(1 row)
而json的输出键的顺序和输入完全一样,如下所示:
mydb=> SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json;
json
-------------------------------------------------
{"bar": "baz", "balance": 7.77, "active":false}
(1 row)
另外,jsonb类型会去掉输入数据中键值的空格,如下所示:
mydb=> SELECT ' {"id":1, "name":"francs"}'::jsonb;
jsonb
-----------------------------
{"id": 1, "name": "francs"}
(1 row)
上例中id键与name键输入时是有空格的,输出显示空格键被删除,而json的输出和输入一样,不会删掉空格键:
mydb=> SELECT ' {"id":1, "name":"francs"}'::json;
json
-------------------------------
{"id":1, "name":"francs"}
(1 row)
另外,jsonb会删除重复的键,仅保留最后一个,如下所示:
mydb=> SELECT ' {"id":1,
"name":"francs",
"remark":"a good guy! ",
"name":"test"
}'::jsonb;
jsonb
----------------------------------------------------
{"id": 1, "name": "test", "remark": "a good guy! "}
(1 row)
上面name键重复,仅保留最后一个name键的值,而json数据类型会保留重复的键值。在大多数应用场景下建议使用jsonb,除非有特殊的需求,比如对json的键顺序有特殊的要求。
jsonb与json操作符
以文本格式返回json类型的字段键值可以使用“->>”操作符,如下所示:
mydb=> SELECT name ->> 'col2' FROM test_json1 WHERE id=1;
?column?
----------
francs
(1 row)
字符串是否作为顶层键值,如下所示:
mydb=> SELECT '{"a":1, "b":2}'::jsonb ? 'a';
?column?
----------
t
(1 row)
删除json数据的键/值,如下所示:
mydb=> SELECT '{"a":1, "b":2}'::jsonb - 'a';
?column?
----------
{"b": 2}
(1 row)
jsonb与json函数
json与jsonb相关的函数非常丰富,下面举例说明。扩展最外层的json对象成为一组键/值结果集,如下所示:
mydb=> SELECT * FROM json_each('{"a":"foo", "b":"bar"}');
key | value
--------+-------
a | "foo"
b | "bar"
(2 rows)
以文本形式返回结果,如下所示:
mydb=> SELECT * FROM json_each_text('{"a":"foo", "b":"bar"}');
key | value
--------+-------
a | foo
b | bar
(2 rows)
一个非常重要的函数为row_to_json()函数,能够将行作为json对象返回,此函数常用来生成json测试数据,比如将一个普通表转换成json类型表,代码如下所示:
mydb=> SELECT * FROM test_copy WHERE id=1;
id | name
-------+------
1 | a
(1 row)
mydb=> SELECT row_to_json(test_copy) FROM test_copy WHERE id=1;
row_to_json
---------------------
{"id":1, "name":"a"}
(1 row)
返回最外层的json对象中的键的集合,如下所示:
mydb=> SELECT * FROM json_object_keys('{"a":"foo", "b":"bar"}');
json_object_keys
------------------
a
b
(2 rows)
jsonb键/值的追加、删除、更新
jsonb键/值追加可通过“||”操作符,例如增加sex键/值,如下所示:
mydb=> SELECT '{"name":"francs", "age":"31"}'::jsonb ||
'{"sex":"male"}'::jsonb;
?column?
------------------------------------------------
{"age": "31", "sex": "male", "name": "francs"}
(1 row)
jsonb键/值的删除有两种方法,一种是通过操作符“-”删除,另一种通过操作符“#-”删除指定键/值,通过操作符“-”删除键/值的代码如下所示:
mydb=> SELECT '{"name": "James", "email": "james@localhost"}'::jsonb
- 'email';
?column?
-------------------
{"name": "James"}
(1 row)
mydb=> SELECT '["red", "green", "blue"]'::jsonb -0;
?column?
-------------------
["green", "blue"]
第二种方法是通过操作符“#-”删除指定键/值,通常用于有嵌套json数据删除的场景,如下代码删除嵌套contact中的fax键/值:
mydb=> SELECT '{"name": "James", "contact": {"phone": "01234 567890", "fax":
"01987543210"}}'::jsonb #- '{contact, fax}'::text[];
?column?
---------------------------------------------------------
{"name": "James", "contact": {"phone": "01234567890"}}
(1 row)
删除嵌套aliases中的位置为1的键/值,如下所示:
mydb=> SELECT '{"name": "James", "aliases": ["Jamie", "The Jamester", "J Man"]}'::jsonb
#- '{aliases,1}'::text[];
?column?
--------------------------------------------------
{"name": "James", "aliases": ["Jamie", "J Man"]}
(1 row)
键/值的更新也有两种方式,第一种方式为“||”操作符,“||”操作符可以连接json键,也可覆盖重复的键值,如下代码修改age键的值:
mydb=> 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表示如果键不存在则添加,create_missing值为false表示如果键不存在则不添加,示例如下:
mydb=> SELECT jsonb_set('{"name":"francs", "age":"31"}'::jsonb, '{age}', '"32"'::jsonb, false);
jsonb_set
---------------------------------
{"age": "32", "name": "francs"}
(1 row)
mydb=> SELECT jsonb_set('{"name":"francs", "age":"31"}'::jsonb, '{sex}', '"male"'::jsonb, true);
jsonb_set
------------------------------------------------
{"age": "31", "sex": "male", "name": "francs"}
(1 row)