源:https://blog.csdn.net/c_zyer/article/details/130968257?ops_request_misc=&request_id=&biz_id=102&utm_term=PostgreSQL%20%E7%9A%84JSON%20%E5%A4%84%E7%90%86&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduweb~default-3-130968257.nonecase&spm=1018.2226.3001.4187
由于项目内使用的Postgresql 且存储了一些非结构化的json数据,里面含有统计与记录,并且有嵌套关系,所以需要了解如何查询和处理Postgresql中的JSON数据。
Postgresql:9.6
官方文档:http://postgres.cn/docs/9.6/functions-json.html#FUNCTIONS-JSON-CREATION-TABLE
一张基础订单表结构:
-- order表
"id" bigserial primary key,
"order_id" varchar(55) COLLATE "default",
"product_id" int8,
"order_json" text COLLATE "default",
"create_time" timestamp(6),
背景知识:json和jsonb 操作符
操作符 | 右操作数类型 | 描述 | 例子 | 例子结果 |
-> | int | 获得 JSON 数组元素(索引从 0 开始,负整数结束) | ‘[{“a”:“foo”},{“b”:“bar”},{“c”:“baz”}]’::json->2 | {“c”:“baz”} |
-> | text | 通过键获得 JSON 对象域 | ‘{“a”: {“b”:“foo”}}’::json->‘a’ | {“b”:“foo”} |
->> | int | 以文本形式获得 JSON 数组元素 | ‘[1,2,3]’::json->>2 | 3 |
->> | text | 以文本形式获得 JSON 对象域 | ‘{“a”:1,“b”:2}’::json->>‘b’ | 2 |
#> | text[] | 获取在指定路径的 JSON 对象 | ‘{“a”: {“b”:{“c”: “foo”}}}’::json#>‘{a,b}’ | {“c”: “foo”} |
#>> | text[] | 以文本形式获取在指定路径的 JSON 对象 | ‘{“a”:[1,2,3],“b”:[4,5,6]}’::json#>>‘{a,2}’ | 3 |
问:如何查看JSON指定的key内容?
通过::json的语法
select order_json::json->'orderBody' from order -- 对象域
select order_json::json->>'orderBody' from order -- 文本
select order_json::json#>'{orderBody}' from order -- 对象域
select order_json::json#>>'{orderBody}' from order -- 文本
额外的jsonb操作符
操作符 | 右操作数类型 | 描述 | 例子 |
@> | jsonb | 左边的 JSON 值是否包含顶层右边JSON路径/值项? | '{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb |
<@ | jsonb | 左边的JSON路径/值是否包含在顶层右边JSON值中? | '{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb |
? | text | 字符串是否作为顶层键值存在于JSON值中? | '{"a":1, "b":2}'::jsonb ? 'b' |
?| | text[] | 这些数组字符串中的任何一个是否作为顶层键值存在? | '{"a":1, "b":2, "c":3}'::jsonb ?| array['b', 'c'] |
?& | text[] | 这些数组字符串是否作为顶层键值存在? | '["a", "b"]'::jsonb ?& array['a', 'b'] |
|| | jsonb | 连接两个jsonb值到新的jsonb值 | '["a", "b"]'::jsonb || '["c", "d"]'::jsonb |
- | text | 从左操作数中删除键/值对或字符串元素。基于键值匹配键/值对。 | '{"a": "b"}'::jsonb - 'a' |
- | integer | 删除指定索引的数组元素(负整数结尾)。如果顶层容器不是一个数组,那么抛出错误。 | '["a", "b"]'::jsonb - 1 |
#- | text[] | 删除指定路径的域或元素(JSON数组,负整数结尾) | '["a", {"b":1}]'::jsonb #- '{1,b}' |
表 9-44展示了能用来创建 json和jsonb值的函数 (当前,没有用于jsonb, row_to_json以及array_to_json的等效函数, 然而,to_jsonb函数提供了许多诸如这些函数的相同功能)。
表 9-44. JSON 创建函数
函数 | 描述 | 例子 | 例子结果 |
to_json(anyelement) to_jsonb(anyelement) | 把值返回为json或者jsonb。数组和组合被(递归地)转换成数组和对象;否则, 如果有从该类型到json的投影,将使用该投影函数来执行转换; 否则将产生一个标量值。对任何一个数值、布尔量或空值的标量类型, 将使用其文本表达,以这样一种方式使其成为有效的json或者jsonb值。 | to_json('Fred said "Hi."'::text) | "Fred said \"Hi.\"" |
array_to_json(anyarray [, pretty_bool]) | 把数组作为一个 JSON 数组返回。一个 PostgreSQL 多维数组会成为一个数组 的 JSON 数组。如果pretty_bool为真,将在 第 1 维度的元素之间增加换行。 | array_to_json('{{1,5},{99,100}}'::int[]) | [[1,5],[99,100]] |
row_to_json(record [, pretty_bool]) | 把行作为一个 JSON 对象返回。如果pretty_bool为真,将在第1层元素之间增加换行。 | row_to_json(row(1,'foo')) | {"f1":1,"f2":"foo"} |
json_build_array(VARIADIC "any") jsonb_build_array(VARIADIC "any") | 从一个可变参数列表构造一个可能包含异质类型的 JSON 数组。 | json_build_array(1,2,'3',4,5) | [1, 2, "3", 4, 5] |
json_build_object(VARIADIC "any") jsonb_build_object(VARIADIC "any") | 从一个可变参数列表构造一个 JSON 对象。通过转换,该参数列表由交替 出现的键和值构成。 | json_build_object('foo',1,'bar',2) | {"foo": 1, "bar": 2} |
json_object(text[]) jsonb_object(text[]) | 从一个文本数组构造一个 JSON 对象。该数组必须可以是具有偶数个成员的 一维数组(成员被当做交替出现的键/值对),或者是一个二维数组(每一个 内部数组刚好有 2 个元素,可以被看做是键/值对)。 | json_object('{a, 1, b, "def", c, 3.5}') json_object('{{a, 1},{b, "def"},{c, 3.5}}') | {"a": "1", "b": "def", "c": "3.5"} |
json_object(keys text[], values text[]) jsonb_object(keys text[], values text[]) | json_object的这种形式从两个独立的数组得到键/值对。在其 他方面和一个参数的形式相同。 | json_object('{a, b}', '{1,2}') | {"a": "1", "b": |
注意: array_to_json和row_to_json与to_json
具有相同的行为,不过它们提供了更好的打印选项。针对to_json所描述 的行为同样也适用于由其他 JSON 创建函数转换的每个值。注意: hstore扩展有一个从hstore到json 的造型,因此通过 JSON 创建函数转换的hstore值将被表示为 JSON
对象,而不是原始字符串值。
表 9-45展示了可用来处理json 和jsonb值的函数。
表 9-45. JSON 处理函数
函数 | 返回值 | 描述 | 例子 | 例子结果 |
json_array_length(json) jsonb_array_length(jsonb) | int | 返回最外层 JSON 数组中的元素数量。 | json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]') | 5 |
json_each(json) jsonb_each(jsonb) | setof key text, value json setof key text, value jsonb | 扩展最外层的 JSON 对象成为一组键/值对。 | select * from json_each('{"a":"foo", "b":"bar"}') | key | value -----+------- a | "foo" b | "bar" |
json_each_text(json) jsonb_each_text(jsonb) | setof key text, value text | 扩展最外层的 JSON 对象成为一组键/值对。返回值将是文本类型。 | select * from json_each_text('{"a":"foo", "b":"bar"}') | key | value -----+------- a | foo b | bar |
json_extract_path(from_json json, VARIADIC path_elems text[]) jsonb_extract_path(from_json jsonb, VARIADIC path_elems text[]) | json jsonb | 返回由path_elems指向的 JSON 值(等效于#>操作符)。 | json_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4') | {"f5":99,"f6":"foo"} |
json_extract_path_text(from_json json, VARIADIC path_elems text[]) jsonb_extract_path_text(from_json jsonb, VARIADIC path_elems text[]) | text | 以文本返回由path_elems指向的 JSON 值(等效于#>>操作符)。 | json_extract_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"foo"}}','f4', 'f6') | foo |
json_object_keys(json) jsonb_object_keys(jsonb) | setof text | 返回最外层 JSON 对象中的键集合。 | json_object_keys('{"f1":"abc","f2":{"f3":"a", "f4":"b"}}') | json_object_keys ------------------ f1 f2 |
json_populate_record(base anyelement, from_json json) jsonb_populate_record(base anyelement, from_json jsonb) | anyelement | 扩展from_json中的对象成一个行,它的列匹配由base定义的记录类型(见下文的注释)。 | select * from json_populate_record(null::myrowtype, '{"a":1,"b":2}') | a | b ---+--- 1 | 2 |
json_populate_recordset(base anyelement, from_json json) jsonb_populate_recordset(base anyelement, from_json jsonb) | setof anyelement | 扩展from_json中最外的对象数组为一个集合,该集合的列匹配由base定义的记录类型。 | select * from json_populate_recordset(null::myrowtype, '[{"a":1,"b":2},{"a":3,"b":4}]') | a | b ---+--- 1 | 2 3 | 4 |
json_array_elements(json) jsonb_array_elements(jsonb) | setof json setof jsonb | 把一个 JSON 数组扩展成一个 JSON 值的集合。 | select * from json_array_elements('[1,true, [2,false]]') | value ----------- 1 true [2,false] |
json_array_elements_text(json) jsonb_array_elements_text(jsonb) | setof text | 把一个 JSON 数组扩展成一个text值集合。 | select * from json_array_elements_text('["foo", "bar"]') | value ----------- foo bar |
json_typeof(json) jsonb_typeof(jsonb) | text | 把最外层的 JSON 值的类型作为一个文本字符串返回。可能的类型是: object、array、string、number、 boolean以及null。 | json_typeof('-123.4') | number |
json_to_record(json) jsonb_to_record(jsonb) | record | 从一个 JSON 对象(见下文的注解)构建一个任意的记录。正如所有返回record 的函数一样,调用者必须用一个AS子句显式地定义记录的结构。 | select * from json_to_record('{"a":1,"b":[1,2,3],"c":"bar"}') as x(a int, b text, d text) | a | b | d ---+---------+--- 1 | [1,2,3] | |
json_to_recordset(json) jsonb_to_recordset(jsonb) | setof record | 从一个 JSON 对象数组(见下文的注解)构建一个任意的记录集合。正如所有返回record 的函数一样,调用者必须用一个AS子句显式地定义记录的结构。 | select * from json_to_recordset('[{"a":1,"b":"foo"},{"a":"2","c":"bar"}]') as x(a int, b text); | a | b ---+----- 1 | foo 2 | |
json_strip_nulls(from_json json) jsonb_strip_nulls(from_json jsonb) | json jsonb | 返回具有空值对象域的from_json。其它空值不变。 | json_strip_nulls('[{"f1":1,"f2":null},2,null,3]') | [{"f1":1},2,null,3] |
jsonb_set(target jsonb, path text[], new_value jsonb[, create_missing boolean]) | jsonb | 如果create_missing是真的 (缺省是true)并且通过path 指定部分不存在,那么返回target, 它具有path指定部分, new_value替换部分, 或者new_value添加部分。 正如路径导向的操作符,负整数出现在JSON数组结尾的path>计数中。 | jsonb_set('[{"f1":1,"f2":null},2,null,3]', '{0,f1}','[2,3,4]', false) jsonb_set('[{"f1":1,"f2":null},2]', '{0,f3}','[2,3,4]') | [{"f1":[2,3,4],"f2":null},2,null,3] [{"f1": 1, "f2": null, "f3": [2, 3, 4]}, 2] |
jsonb_insert(target jsonb, path text[], new_value jsonb, [insert_after boolean]) | jsonb | 返回被插入了new_value的target。 如果path指定的target 节在一个 JSONB 数组中,new_value 将被插入到目标之前 (insert_after为false,默认情况) 或者之后(insert_after为真)。 如果path指定的target 节在一个 JSONB 对象内,则只有当target 不存在时才插入new_value。对于面向路径的操作符来说, 出现在path中的负整数表示从 JSON 数组的末尾开始计数。 | jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"') jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true) | {"a": [0, "new_value", 1, 2]} {"a": [0, 1, "new_value", 2]} |
jsonb_pretty(from_json jsonb) | text | 作为缩进JSON文本返回from_json。 | jsonb_pretty('[{"f1":1,"f2":null},2,null,3]') | [ { "f1": 1, "f2": null }, 2, null, 3 ] |
常用的比较操作符
操作符 | 描述 |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
= | 等于 |
<> or != | 不等于 |
问:怎么处理多层嵌套的JSON?
就是采用基本的JSON语法,注意结果是对象域还是文本,对象域可以继续取用字段,文本就不能继续查看JSON咯
select '{"sites":{"site":{"id":"1","name":"菜鸟教程","url":"www.runoob.com"}}}'::json->'sites'->'site' -- 对象域
select '{"sites":{"site":{"id":"1","name":"菜鸟教程","url":"www.runoob.com"}}}'::json->'sites'->>'site' -- 文本
问:怎么处理JSON数组呢?
也是通过JSON的基本操作先定位到数组对象所在的Key,通过key取到对应的value后直接->(0),就可以取用到对应的对象域,注意对象域和文本,转化为文本就不能够在取key和具体数据数据咯
还有很多关于json相关的方法,可以详见官方文档
select '{"sites":{"site":[{"id":"1","name":"菜鸟教程","url":"www.runoob.com"},{"id":"2","name":"菜鸟工具","url":"c.runoob.com"},{"id":"3","name":"Google","url":"www.google.com"}]}}'::json->'sites'->'site'->(0)
select '{"sites":{"site":[{"id":"1","name":"菜鸟教程","url":"www.runoob.com"},{"id":"2","name":"菜鸟工具","url":"c.runoob.com"},{"id":"3","name":"Google","url":"www.google.com"}]}}'::json->'sites'->'site'->(0)->>'id'
延伸:如何取用JSON数组的最后一个对象数据?
select json_array_length('{"sites":{"site":[{"id":"1","name":"菜鸟教程","url":"www.runoob.com"},{"id":"2","name":"菜鸟工具","url":"c.runoob.com"},{"id":"3","name":"Google","url":"www.google.com"}]}}'::json->'sites'->'site') -- 查询json数据的长度
select '{"sites":{"site":[{"id":"1","name":"菜鸟教程","url":"www.runoob.com"},{"id":"2","name":"菜鸟工具","url":"c.runoob.com"},{"id":"3","name":"Google","url":"www.google.com"}]}}'::json->'sites'->'site'->(json_array_length('{"sites":{"site":[{"id":"1","name":"菜鸟教程","url":"www.runoob.com"},{"id":"2","name":"菜鸟工具","url":"c.runoob.com"},{"id":"3","name":"Google","url":"www.google.com"}]}}'::json->'sites'->'site') -1)
问:怎么替换JSON字符串中的内容?
通过select语句先看一下官方语法
函数 | 返回值 | 描述 | 例子 | 例子结果 |
jsonb_set(target jsonb, path text[], new_value jsonb[, create_missing boolean]) | jsonb | 如果create_missing是真的 (缺省是true)并且通过path 指定部分不存在,那么返回target, 它具有path指定部分, new_value替换部分, 或者new_value添加部分。 正如路径导向的操作符,负整数出现在JSON数组结尾的path>计数中。 | (1)jsonb_set(‘[{“f1”:1,“f2”:null},2,null,3]’, ‘{0,f1}’,‘[2,3,4]’, false) (2)jsonb_set(‘[{“f1”:1,“f2”:null},2]’, ‘{0,f3}’,‘[2,3,4]’) | [{“f1”:[2,3,4],“f2”:null},2,null,3] [{“f1”: 1, “f2”: null, “f3”: [2, 3, 4]}, 2] |
参照官方文档,简单的一次内容替换
select jsonb_set(order_json::jsonb,'{premsg}','test'::jsonb) from order
那么如果是嵌套多层的JSON value可以替换吗?–可以的,语法是一样的,就是需要定位到指定的字段就可以
select jsonb_set((order_json::json->>'rspDesc')::jsonb, '{preOrder}', '"11111"'::jsonb) from order
上面是select语句,那具体的update语句怎么写呢?
语法:UPDATE 表明 set 列名 = (jsonb_set(列名::jsonb,'{key}','"value"'::jsonb)) where 条件
update order set order_json = jsonb_set(order_json::jsonb,'{rspDesc}',(jsonb_set((event_json::json->>'rspDesc')::jsonb, '{preNumber}', '"999999999"'::jsonb)::jsonb)) -- 需要先把需要改的内容替换好,然后在整体更新替换,此时这个rspDesc是对象域格式
网上基本没有执行成功的例子,在此记录下。
最后再贴一个成功的例子:
PG table: “public”.“MachineData”
Copy 一个MsgData供参考。
{
"CAMXEvent": {
"MessageInfo": {
"dateTime": "2023-04-20T:16:01:35+0800",
"sender": "M35-AOI2",
"senderType": "MACHINE",
"destination": "CGSSERVER",
"messageId": "CNWUXPRD0441-1681977720860-19202",
"messageSchema": "2547/ProcessStepStatus.xsd"
},
"ProcessStepStatus": {
"dateTime": "2023-04-20T:16:01:35+0800",
"itemInstanceId": "JJM35123042001169",
"sessionRef": "M35-AOI2-JJM35123042001169-2023-04-20T16:01:35+08:00",
"itemProcessRef": "M35-AOI2-JJM35123042001169-2023-04-20T16:01:35+08:00",
"processStepId": "AOI",
"status": "PASSED",
"imageId": "0",
"userName": "admin",
"isRerun": "false",
"Indictment": [
{
"indictmentId": "PASTE",
"indictmentKey": "Paste",
"description": "Paste",
"regionRef": "c8-95",
"falseClaim": "false",
"Component": {
"componentId": "AJ131S00063"
}
},
{
"indictmentId": "RIGHT OFF",
"indictmentKey": "Right off",
"description": "Right off",
"regionRef": "u1-90",
"falseClaim": "false",
"Component": {
"componentId": "AJ343S00305"
}
},
{
"indictmentId": "HOR OFF",
"indictmentKey": "Hor off",
"description": "Hor off",
"regionRef": "r1-92",
"falseClaim": "false",
"Component": {
"componentId": "AJ117S0156"
}
},
{
"indictmentId": "HOR OFF",
"indictmentKey": "Hor off",
"description": "Hor off",
"regionRef": "r1-91",
"falseClaim": "false",
"Component": {
"componentId": "AJ117S0156"
}
},
{
"indictmentId": "RIGHT OFF",
"indictmentKey": "Right off",
"description": "Right off",
"regionRef": "u1-96",
"falseClaim": "false",
"Component": {
"componentId": "AJ343S00305"
}
},
{
"indictmentId": "RIGHT OFF",
"indictmentKey": "Right off",
"description": "Right off",
"regionRef": "u1-95",
"falseClaim": "false",
"Component": {
"componentId": "AJ343S00305"
}
},
{
"indictmentId": "RIGHT OFF",
"indictmentKey": "Right off",
"description": "Right off",
"regionRef": "u1-93",
"falseClaim": "false",
"Component": {
"componentId": "AJ343S00305"
}
},
{
"indictmentId": "HOR OFF",
"indictmentKey": "Hor off",
"description": "Hor off",
"regionRef": "u1-69",
"falseClaim": "false",
"Component": {
"componentId": "AJ343S00305"
}
},
{
"indictmentId": "RIGHT OFF",
"indictmentKey": "Right off",
"description": "Right off",
"regionRef": "u1-47",
"falseClaim": "false",
"Component": {
"componentId": "AJ343S00305"
}
}
],
"Extensions": {
"ProcessStepStatusInformation": {
"source": "MACHINE",
"equipmentName": "M35-AOI2"
}
}
}
}
}
执行SQL
SELECT
"EMT",
"MsgData" :: json #>> '{CAMXEvent, ProcessStepStatus,itemInstanceId}' AS SerialNumber,
"MsgData" :: json #>> '{CAMXEvent, ProcessStepStatus,status}'AS status,
"EventTime"
FROM
"public"."MachineData"
WHERE
1 = 1
AND "EMT" IN ( '01000007', '01000009' )
AND "EventType" = 'ProcessStepStatus'
and "MsgData" :: json #>> '{CAMXEvent, ProcessStepStatus,status}' = 'FAILED'
LIMIT 10 OFFSET 0