PostgerSQL技术问答10 - JSON

本文是《PostgreSQL技术问答》系列文章中的一篇。关于这个系列的由来,可以参阅开篇文章:

《PostgreSQL技术问答00 - Why Postgres》

文章的编号只是一个标识,在系列中没有明确的逻辑顺序和意义。读者进行阅读时,不用太关注这个方面。

本文讨论的内容是PostgreSQL对于JSON的支持。

什么是JSON

这里简单复习一下。JSON,全意为JavaScript Object Notation,即JavaScrip对象表示。顾名思义,它原来就是JavaScript语言使用的一种对象的呈现方式。它使用一种结构化的字符串形式,来对数据对象进行表达数据。所以,对人类而言,它同样易于阅读和编写。下面是一个简单的例子(来自JSON WIKI):

sql 复制代码
{
  "first_name": "John",
  "last_name": "Smith",
  "is_alive": true,
  "age": 27,
  "address": {
    "street_address": "21 2nd Street",
    "city": "New York",
    "state": "NY",
    "postal_code": "10021-3100"
  },
  "phone_numbers": [
    {
      "type": "home",
      "number": "212 555-1234"
    },
    {
      "type": "office",
      "number": "646 555-4567"
    }
  ],
  "children": [
    "Catherine",
    "Thomas",
    "Trevor"
  ],
  "spouse": null
}

完整的JSON格式定义,我们可以参考下列官方技术文档:www.json.org/json-zh.htm...

JSON的定义和规则如此简单,只使用一张图就可以概括和总结(来自Wiki):

这里稍微用文字可以总结一下:

  • JSON使用结构化的字符串来表达对象和数组
  • 对象的基本形式是大括号 "{}"围起来的键值对,键值使用冒号分隔,键值对以逗号分隔
  • 对象的键必须是一个字符串
  • 对象的值可以是各种数据类型包括null、布尔、数值、字符串、对象和数组等
  • 字符串和键使用""包括来表示
  • 数值可以包括正负整数和浮点数
  • 布尔包括true和false关键字来表示
  • 数组的基本形式是中括号 "[]"围起来的有序的对象或者值列表,以逗号分隔
  • 数组的内容,可以是对象的值的各种形式
  • 可以在对象和数组之间进行嵌套
  • JSON字符串使用UTF-8编码

相比传统编程语言的二进制数据对象结构和同样基于字符串文本的XML等表示方式,它具有以下特点:

  • 完全公开的定义和标准
  • 简单直观,轻量化
  • 同时易于人类的编写阅读,和程序的自动化解析和处理
  • 方便进行跨平台的存储和网络的传输
  • 原生的JavaScript支持

基于上面的形式和特点,以及它在Web应用开发中的广泛使用,JSON已经成为Web应用开发的一个事实上的标准,被广泛的应用。

当然,世界上没有完美的事物,JSON也不能例外。JSON在技术上的不足之处主要是:

  • 字符串解析和处理,相对二进制结构体效率稍低
  • 缺乏对时间日期、二进制数据的原生支持(虽然可以使用数值和Base64字符串处理)
  • 没有对原生大数值的支持
  • 一般的JSON解析实现,不保证键的次序
  • 数据压缩不好实现或者处理
  • 不能在内部保证数据和格式的完备,和冗余处理
  • 不支持注释信息

为什么Postgres要支持JSON

笔者认为,简单的说,就是适应Web应用开发技术的发展。现代化的网络业务应用系统,基本上都是Web应用系统。就是整个系统大体体现为客户端和服务器的范式。而客户端基本上已经完全标准化,就是使用Web浏览器技术作为其标准客户端和技术基础。而在浏览器中,则使用JavaScript作为主要编程和应用语言。JSON可以看成是JS语言的一个组成部分,所以,JSON就是Web技术中,事实上的用于结构化数据表示的技术标准,被广泛的应用到数据表示、数据存储、传输和交换、配置信息等各种各样的应用场景当中。

顺应这个Web应用发展的潮流,主流的关系型数据库系统,都逐渐的实现了对JSON数据结构和处理的支持,来提高数据存储和处理的包容性,提升系统的开发效率和兼容性。PG是其中发展的比较早的,从9.2版本(大约是2012年)开始,我们就看到了相关特性的支持。我们现在还可以在技术文档中看到相关的内容,当然那时候的特性还是非常简陋,现在对JSON比较完善和丰富的特性,也是在后续长期的演进过程中发展起来的。

除了对Web应用的支持之外,对于JSON的支持,对于关系数据库之外,可能还意味着一个重大的范式转换,就是打破和扩展了传统关系数据基于"表",就是行和列的基本结构的限制,引入了可变的数据结构。这样显然可以提高对于业务和应用的支持的灵活性,更好的适应了现在应用程序敏捷开发和快速演进的发展和应用模式。

例如,在原来的数据库应用系统系统,需要在开发前,定义好数据结构。比如一个用户,它有标识、名称和联系方式等属性,通常使用对应的数据字段来标识。但问题是,不同的用户,他可能具有的联系方式差异很大,如不同的社交平台用户、很多种电话号码等等,JSON可以按照需求设计这个结构,而且很容易修改和扩展,就比传统的数据库字段定义要方便灵活很多。

在最近的几个版本中,JSON已经成为SQL的标准。同样,有一个发展的过程,而且仍然在发展过程当中。

什么是JSONB

我们在Postgres数据库中,更常见和推荐使用的JSON格式,并不是原生的JSON字符串,而是JSONB,即JSON的Binary(二进制)形式。PostgreSQL支持使用JSONB,可能主要出于以下考量:

  • 二进制格式存储数据,而不是文本格式,可以使存储更加紧凑,处理更加高效
  • JSONB支持GIN (Generalized Inverted Index) 索引,可以显著提高查询性能
  • JSONB自动去除重复的对象键,保证无重复键
  • JSONB可以保留键顺序
  • PG专门针对JSONB数据,进行相关操作和处理的优化

为此,PostgreSQL还提供了响应的JSONB的信息存储和处理的机制,例如提供了多种操作符和函数来处理JSONB数据,让用户可以之间使用SQL来访问和操作JSONB数据,并且支持很大复杂的数据查询和分析操作。

对于使用和开发而言,使用JSONB几乎是透明的,用户基本无法感知使用JSONB和JSON对象之间的区别。例如Nodejs配套的PG程序库,可以直接处理JSONB对象,比如查询时,可以直接从JSONB数据中,转换到JS环境中的JS对象。大大提高了开发效率。同样,我们下面的讨论内容和示例,如果不是特别说明,JSON和JSONB在应用方面,是基本没有什么差异的。

PostgreSQL如何实现对JSON的支持

在PostgreSQL中,对于JSON(JSONB)的应用,包括下列常用的场景和用法:

字段数据类型

在创建表时,可以为字段定义和使用JSON的数据类型。

sql 复制代码
CREATE TABLE jusers (
    id SERIAL PRIMARY KEY,
    data JSONB
);
CREATE TABLE

\d jusers;
                            Table "public.jusers"
 Column |  Type   | Collation | Nullable |              Default               
--------+---------+-----------+----------+------------------------------------
 id     | integer |           | not null | nextval('jusers_id_seq'::regclass)
 data   | jsonb   |           |          | 
Indexes:
    "jusers_pkey" PRIMARY KEY, btree (id)
    

插入数据

下面的SQL语句,可以像插入普通记录一样,插入JSON数据,注意这里使用字符串(单引号包围)来表达JSON对象。

sql 复制代码
defaultdb=> INSERT INTO jusers (id,data) VALUES (1001, '{"name": "John", "age": 30}');
INSERT 0 1
defaultdb=> INSERT INTO jusers (id,data) VALUES (1002, '{"name": "Tom", "gender": 1}');
INSERT 0 1
defaultdb=> select * from jusers;
  id  |             data             
------+------------------------------
 1001 | {"age": 30, "name": "John"}
 1002 | {"name": "Tom", "gender": 1}
(2 rows)

数据查询

当然,JSON数据可以作为普通字符串进行查询,但这样就没有什么意义了。PG提供了一套语法和机制,来方便开发者使用和查询JSON数据。下面是一个简单的例子:

sql 复制代码
> select data->>'name' name, data->>'gender' gender, data->'age' age from jusers;
 name | gender | age 
------+--------+-----
 John |        | 30
 Tom  | 1      | 
(2 rows)

可能由于SQL语义的一些限制,PG不能使用普通编程语言中处理JSON的"."和方括号等的表示和引用方法,而是重新构造了一套操作符来处理,确实也是带来了一些学习曲线,我们后面有更详细的列举和说明。

除此之外,JSON信息和普通的数据库字段的使用方式差异不大,可以正常用在字段列表、条件检查等常用的数据查询场合。

数据修改

前面探讨了PG中,对应JSON字段属性的引用方式和规则。很直观的,我们就可以想象可以使用这个方式来进行属性的修改。但很遗憾,Postgres无法支持这个想法,它不能直接对JSON字段的属性进行修改,而只能处理整个JSON对象。示例如下:

sql 复制代码
-- 错误的修改
update jusers set data->>'gender' = 1 where id = 1001;
ERROR:  syntax error at or near "->>"
LINE 1: update jusers set data->>'gender' = 1 where id = 1001;

-- 正确的修改
update jusers set data = data || '{"gender":1}'::jsonb 
where data->>'name' = 'John' returning *;
  id  |                   data                   
------+------------------------------------------
 1001 | {"age": 30, "name": "John", "gender": 1}
(1 row)

UPDATE 1

-- 删除属性 - 使用操作符
update jusers set data = data - 'gender' 
where data->>'name' = 'John' returning *;
  id  |            data             
------+-----------------------------
 1001 | {"age": 30, "name": "John"}
(1 row)

UPDATE 1

-- jsonb_set函数,增加或修改路径上的对象
update jusers set data = jsonb_set( data, '{address}', '"ChengDu"' ) 
where data->>'name' = 'John' returning *;
  id  |                       data                        
------+---------------------------------------------------
 1001 | {"age": 30, "name": "John", "address": "ChengDu"}
(1 row)

UPDATE 1

-- insert函数,插入值到JSON数组中
select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"') ;
         jsonb_insert          
-------------------------------
 {"a": [0, "new_value", 1, 2]}
(1 row)

要修改一个JSON字段的属性,它的基本处理方式是整个修改这个字段完整的JSON字符串。当然我们一般不会这样做,通常只修改其中某个属性的内容,这时可以使用"合并"的方式,即引用原来的对象,合并新的属性。这个操作方法可以处理增加或者修改属性的情况,因为JSON当遇到重复的键的时候,它只接收最后的键值定义。

如果要删除JSON的键或者数组值,可以使用 "-" 操作符。如果要增加键,或者修改键上的值,还可以使用json_set函数。如果要增加值到JSON数组中,可以使用json_insert函数。

构造和转换

前面了解了使用字符串来表示JSON的方法。除此之外,PG还支持很多方法用不同的方式来构造JSON数据。下面是一些简单的例子:

sql 复制代码
-- json_build_object方法, 键-值数组转换为JSON
defaultdb=> SELECT json_build_object('name', 'John', 'age', 30);
       json_build_object       
-------------------------------
 {"name" : "John", "age" : 30}
(1 row)

-- json_object,由JSON对象,灵活构建
 select json_object('{a,b}', '{1,2}')
 union all
 select json_object('{a, 1, b, "def", c, 3.5}')
 union all
 select json_object('{{a, 1}, {b, "def"}, {c, 3.5}}') ;
              json_object              
---------------------------------------
 {"a" : "1", "b" : "2"}
 {"a" : "1", "b" : "def", "c" : "3.5"}
 {"a" : "1", "b" : "def", "c" : "3.5"}
(3 rows)

-- json_build_array,列表值构造JSON数组
select json_build_array(1, 2, 'foo', 4, 5);
  json_build_array   
---------------------
 [1, 2, "foo", 4, 5]
(1 row)

-- to_json/to_jsonb方法, 
select to_jsonb(row(42, 'Fred said "Hi."'::text));
               to_jsonb                
---------------------------------------
 {"f1": 42, "f2": "Fred said \"Hi.\""}
(1 row)

-- row_to_json, 将一个行对象或者记录转换为JSON
select row_to_json(row(1,'foo'));
     row_to_json     
---------------------
 {"f1":1,"f2":"foo"}
(1 row)

select row_to_json(jusers) from jusers where id = 1001;
                         row_to_json                         
-------------------------------------------------------------
 {"id":1001,"data":{"age": 30, "name": "John", "gender": 2}}
(1 row)

-- array_to_json, 数组转换为JSON
select array_to_json('{{1,5},{99,100}}'::int[]);
  array_to_json   
------------------
 [[1,5],[99,100]]
(1 row)

-- json_agg, JSON对象的聚合,返回的是一条记录,字段内容是JSON数组
 select json_agg(jusers), json_agg(id) from jusers;
                           json_agg                            |   json_agg   
---------------------------------------------------------------+--------------
 [{"id":1002,"data":{"name": "Tom", "gender": 1}},            +| [1002, 1001]
  {"id":1001,"data":{"age": 30, "name": "John", "gender": 2}}] | 
(1 row)

除了构造之外,常见的操作,就是将JSON转换为标准记录集,如下面的操作:

sql 复制代码
-- JSON转换为记录
select * from json_to_recordset('[{"a":1,"b":"foo"}, {"a":"2","c":"bar"}]') as x(a int, b text);
 a |  b  
---+-----
 1 | foo
 2 | 
(2 rows)

PG中,关于JSON特性的内容很多,这里由于篇幅的限制,只能挑选一些基础和常用的方面进行探讨。完整的JSON功能特性和使用方式,可以参见PG官方技术文档的相关章节:

www.postgresql.org/docs/15/fun...

JSON有那些引用操作符

前面我们已经看到了JSON可以使用"->>"操作符,来对JSON对象的属性进行引用。但实际上,PG定义了很多类似的操作符,来满足不同场景的需求,而且它们之间可能只有很细微的差别,需要开发者在使用中熟悉和区分。

  • json -> integer → json/jsonb

用于JSON数组,可以使用 -> 符号,结合一个索引数字,来引用JSON数组的元素。引用的索引,从0开始,结果是一个JSON对象或者值。

sql 复制代码
with J(v) as (values ('[{"a":"foo"},{"b":"bar"},{"c":"baz"}, "some value"]'::json)) 
select v -> 2, v->3, v->5 from  J;

 ?column?   |   ?column?   | ?column? 
-------------+--------------+----------
 {"c":"baz"} | "some value" | 
(1 row)
  • json/jsonb ->> integer → text

要特别注意这个 "->>" 操作符,它的意思是,返回值是一个标量,如text或者interger等。

  • json -> text → json/jsonb

用于JSON对象,使用字符串作为键进行引用匹配的值,结果是一个JSON或者值。

sql 复制代码
with J(id, jdata) as (values 
(1001, '{"name": "John", "gender": 1, "contact": { "email": "user@mail.com" }}'::jsonb),
(1002, '{"name":"Mary", "gender":2, "age": 28 , "contact":{ "mobile": "13800138000" }}'::jsonb)) 
select id, jdata ->> 'name', jdata -> 'contact' ->> 'email', jdata ->> 'contact' from  J;
  id  | ?column? |   ?column?    |          ?column?          
------+----------+---------------+----------------------------
 1001 | John     | user@mail.com | {"email": "user@mail.com"}
 1002 | Mary     |               | {"mobile": "13800138000"}
(2 rows)

这里面的要点是,如果结果是一个JSON,它还可以继续引用,而如果是一个字符串,显然是不行的。

  • json/jsonb ->> text → text

同理, 使用 "->>" 操作符,得到的是一个标量(字符串)。

  • json #> text[] → json/jsonb

这个用于使用路径进行引用,结果是一个JSON对象或者值。这里使用一个JSON格式的对象,来表示路径层次。

sql 复制代码
select data #> '{a,b,1}',data #>> '{a,b,1}'  
from lateral (values ( '{"a": {"b": ["foo","bar"]}}'::json)) as D(data);
 ?column? | ?column? 
----------+----------
 "bar"    | bar
(1 row)

这里的 a-b-1,就是引用路径,对于对象而言就是键字符串,对于数组而言就是索引(从零开始)。注意这里的数据类型和表达方式是Postgres Array而非JSON Array,这个写法的好处是可以简化深层次引用,并且可以使用标准的JSON方式来构造路径。

  • json/jsonb #>> text[] → text

和上面的例子相似,但得到的是一个标量。例子代码中,也可以看到两者的细微区别(注意结果中的双引号,表示它是一个JSON类型的字符串)。

除了上面常用的引用操作符之外,其他常见使用操作符,对JSON字段进行的操作还包括:

  • jsonb @> jsonb → boolean, 数据包含检查

检查JSON是否包含或者被包含(<@)某个属性。如:

'{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb → t

  • jsonb ? text → boolean

检查JSON对象是否包含某个键,或者JSON数组是否包括某个值,针对第一层。

'{"a":1, "b":2}'::jsonb ? 'b' → t ; '["a", "b", "c"]'::jsonb ? 'b' → t

  • jsonb ?| text[] → boolean

检查JSON对象或数组是否包括文本数组中相同的键或者元素。也是针对第一层。

'{"a":1, "b":2, "c":3}'::jsonb ?| array['b', 'd'] → t

  • jsonb ?& text[] → boolean

检查是否文本数组中,所有的元素,都是JSON对象的键,或者都在JSON数组中(第一层)。

'["a", "b", "c"]'::jsonb ?& array['a', 'b'] → t

  • jsonb || jsonb → jsonb

合并两个JSON对象或者数组。需要注意,操作的元素都是JSON对象,如果是标量,可能需要先转换一下。这个功能经常用作给JSON对象添加属性,或者扩展JSON数组。

sql 复制代码
defaultdb=> select '[1,2]'::jsonb || '3'::jsonb || '[5,6]'::jsonb;
    ?column?     
-----------------
 [1, 2, 3, 5, 6]
(1 row)
  • jsonb - text → jsonb/ jsonb - text[] → jsonb

从对象或数组中删除键或者值。如

'{"a": "b", "c": "d"}'::jsonb - 'a' → {"c": "d"}

'["a", "b", "c", "b"]'::jsonb - 'b' → ["a", "c"]

'{"a": "b", "c": "d"}'::jsonb - '{a,c}'::text[] → {}

  • jsonb - integer → jsonb

从JSON数组中,删除索引所在的值。

'["a", "b", "c"]'::jsonb - 1 → ["a","c"]

  • jsonb #- text[] → jsonb

删除JSON路径上的元素。

'["a", {"b":1}]'::jsonb #- '{1,b}' → ["a", {}]

  • jsonb @? jsonpath → boolean

检查JSON对象中,json路径上是否存在有效值。这里使用jsonpath表达形式

'{"a":[1,2,3,4,5]}'::jsonb @? '$.a[*] ? (@ > 2)' → t

  • jsonb @@ jsonpath → boolean

对JSON对象执行JSON路径谓词检查,并返回第一项的结果。如果结果不是布尔值,则返回NULL。

'{"a":[1,2,3,4,5]}'::jsonb @@ '$.a[*] > 2' → t

在PG中,如何处理JSON数据

前一章节的内容,主要从操作符的角度,来讨论对于JSON对象的操作。除了操作符之外,PG还提供了很多相关的函数。 这里处理的意思是基于数据库中的JSON字段和对象,进行相关的计算和转换,来满足一些常见的业务需求。这里的内容比较多,只简单列举函数的形式和功能,不再举例展开说明。

其中比较重要和常见的函数包括:

  • json_array_elements, 将JSON数组,展开为数据记录集,类似unnest,字段类型为JSON
  • json_array_elements_text,和前面类似,但数据类型为TEXT
  • json_array_length, 获得JSON数组长度
  • json_each/json_each_text,将JSON数组,展开(遍历)成为key/value的记录集形式,text为其值文本版本
  • json_extract_path /json_extract_path_text, 使用路径数组的方式,访问JSON对象路径中的值
  • json_object_keys/ json_object_keys_text, 获取第一层的键值,并展开为多条记录
  • json_populate_record, 将JSON对象,展开为键作为字段名的一套记录,记录字段值为键对应的值
  • json_to_record/json_to_recordset,JSON对象转换为记录和记录集
  • jsonb_insert,在JSON对象或数组中,指定路径位置上,插入JSON对象或者值
  • jsonb_set/jsonb_set_lax, 替换JSON对象,路径位置上,元素的值
  • json_strip_nulls/jsonb_strip_nulls, 可以清理对象中,值为null的项目,但不会影响数组中的null值
  • jsonb_pretty, 按照换行和缩进方式显示JSON字符串,可读性更强
  • json_typeof/json_typeof: 检查JSON值的数据类型,输出为字符串,如null, string, number...
  • jsonb_path_xxx, JSON Path相关功能,这部分的内容,笔者会另行撰文讨论

这些JSON函数,一般都有JSONB的版本,可以处理JSONB类型的数据。

什么是JSON路径

我们在很多JSON方法中,都可以看到这两个参数: path text[], 和 path jsonpath,这两个其实都是表示在JSON对象中,进行属性值查询的方式,称为path路径。

但在不同的函数中,需要的路径的类型不同,一类是 path text[],就是一个字符串类型的数组。例如下面这个jsonb_set函数,就使用text[]作为路径表示来进行属性值的设置:

sql 复制代码
 select jsonb_set('[{"f1":1,"f2":null},2]', '{0,f3}', '[2,3,4]');
                  jsonb_set                  
---------------------------------------------
 [{"f1": 1, "f2": null, "f3": [2, 3, 4]}, 2]
(1 row)

上面这个例子要进行的操作的意思是, 将JSON对象(这里是数组),第0个元素的"f3"的键值设为 [2,3,4]。因为原来这个元素没有f3键,本操作会增加一个键并且赋值。这里的 0-f3就是路径数组的值。

另一类的数据类型是jsonpath,它是使用一个字符串表示的寻址方式,在PG中有专门设计的语法和格式,也可以提供更强大的寻址功能如模式匹配和条件判断等等。也被称为SQL/JSON Path Language,这一部分内容比较多,笔者有另行撰文讨论。

Postgers如何保证JSON处理的效率和性能

在数据库中支持JSON数据类型,一个显而易见的问题就是对性能带来的负面影响。虽然本质上JSON就是一个字符串,但由于不能使用结构化的形式进行处理,就不能像普通的标量字段一样,来保证查询和操作的性能。

简单而言,Postgres为提高JSON信息的处理性能,应用了如下一些优化措施和方法:

  • JSONB存储格式

PG可以选择使用JSONB作为JSON的数据存储格式,这通常不会对应用产生影响。但JSONB数据可以通过合理的压缩和数据去重而更加紧凑,节省存储的空间。同时JSONB可能可以减少数据的编解码转换工作,从而提高处理效率。

  • 索引支持

Postgres中,可以支持直接对JSON属性设置索引。在这里,和普通的字段索引没有太大区别。

作为普遍的解决方案,除了对于特定属性之外,PostgreSQL还支持对JSON和JSONB数据使用GIN或者GiST索引,来提高数据访问和查询的性能。一般认为,GIN索引适用于包含多个键的JSON文档,而GiST索引则适用于包含少量键的JSON文档。开发者应当根据自己的使用场景进行评估和使用。

  • 并行处理

从PostgreSQL 9.6版本开始,PG支持并行序列化执行和并行工作进程,从而实现了JSON数据的并行处理,以提高查询性能。

即便如此,由于需要处理额外的结构和信息,数据库系统对于JSON数据的处理,也会付出相对标量数据更高的代价,所以在使用JSON时,需要额外注意数据和结构的设计,避免不必要的信息和处理过程,做好性能评估和优化工作。

在Postgres中使用JSON有什么需要注意的问题

根据笔者在Postgres中研究和使用JSON的经验,觉得在这个过程中,需要注意下列问题:

  • 学习曲线

由于Postgres对JSON的设计实现主要通过两种方式,就是操作符和函数,而且由于功能的复杂性和完善性,Postgres引入了很多新的表示方式,所以看起来这方面的内容是很多的。这些给开发者的记忆、学习、理解和应用都带来了一些负担。例如,要用好这些JSON的功能,就需要开发者记忆和理解很多规则性的符号和函数名称,并且理解和辨识这些操作之间的差异,以及它们合适的应用场景。

  • 要特别注意数据类型

Postgres相关JSON的操作符和函数的定义比较严格和复杂,要特别注意其参数的数据类型,否则可能不能得到期望的结果。但这个问题并不是特别难处理,一般情况下合理的进行数据类型转换就可以了。

  • 性能问题

JSON带来的性能问题更加复杂,特别是数据规模比较大的时候,需要更加细致的分析和解决。如如果数据结构和属性项目相对稳定,可以考虑选择一般索引,或者GIN索引。

小结

本文探讨了Postgres作为一个强大的Web应用支撑系统,所实现和具备的一个丰富和强大的功能集合-JSON。研究了在PG应用中是如何实现对JSON的支持的,其基本的形态和应用场景,相关的操作符和函数,以及需要注意的问题等等。

相关推荐
努力算法的小明2 分钟前
SQL 复杂查询
数据库·sql
Alive~o.05 分钟前
Go语言进阶&依赖管理
开发语言·后端·golang
斗-匕5 分钟前
MySQL 三大日志详解
数据库·mysql·oracle
许苑向上10 分钟前
Dubbo集成SpringBoot实现远程服务调用
spring boot·后端·dubbo
代码中の快捷键11 分钟前
MySQL数据库存储引擎
数据库·mysql
只因在人海中多看了你一眼11 分钟前
数据库体系
数据库
尘浮生33 分钟前
Java项目实战II基于微信小程序的电影院买票选座系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
六月闻君1 小时前
MySQL 报错:1137 - Can‘t reopen table
数据库·mysql
SelectDB技术团队1 小时前
兼顾高性能与低成本,浅析 Apache Doris 异步物化视图原理及典型场景
大数据·数据库·数据仓库·数据分析·doris
郑祎亦1 小时前
Spring Boot 项目 myblog 整理
spring boot·后端·java-ee·maven·mybatis