说点Oracle的JSON使用的注意事项
JSON这种数据类型在关系型数据库中已经是标配了。如果不是深度应用比如游戏那种的确是离开不了MongoDB,其他的一些简单场景的话Oracle MySQL PostgreSQL都有JSON。今天仅仅说一下Oracle的JSON在使用上的一些问题。
开发人员提出他的场景中需要使用JSON(这个之前我推荐过,现在Oracle和MySQL的JSON都普遍使用,毕竟有了这个减少了数据库的表的变更也是好的)。我的原则是不参与业务逻辑的、不参与运算的、不用来当where条件的可以放在JSON中,反之不要放进来。不过这次开发给我提出了一个问题,就是看到官方介绍(开发能去看数据库官方介绍,其实很不容易,为这位开发同学点赞。我都没怎么去看过)
考虑到性能问题,开发打算用Varchar2的数据类型。不过这个类型有限制,只有4K大小。开发表示也够用。只是想问问万一超过4K怎么做?我第一反应就是用CLOB,因为我在Oracle12C的时候就是用CLOB来完成JSON的。那么这个是不是可以改变类型呢?做个实验。
SQL> create table x (id int,j varchar2(4000) CONSTRAINT ensure_json CHECK (j IS JSON));
表已创建。
SQL> desc x;
名称 是否为空? 类型
----------------------------------------- -------- ----------------------------
ID NUMBER(38)
J VARCHAR2(4000)
创建了一个VARCHAR2类型的JSON的表。去写入一条数据。
SQL> insert into x values (1,'{"tel" : "137", "company": "ouyeel", "name" : "张","address":"宝山"}');
已创建 1 行。
SQL> commit;
提交完成。
SQL> select * from x;
ID
----------
J
--------------------------------------------------------------------------------
1
{"tel" : "137", "company": "ouyeel", "name" : "张","address":"宝山"}
这里要注意,表名一定要有别名,当有了别名t以后查询正常输出。
SQL> select j.tel from x;
select j.tel from x
*
第 1 行出现错误:
ORA-00904: "J"."TEL": 标识符无效
SQL> select x.j.tel from x;
select x.j.tel from x
*
第 1 行出现错误:
ORA-00904: "X"."J"."TEL": 标识符无效
SQL> select t.j.tel from x t;
TEL
--------------------------------------------------------------------------------
137
再创建一个CLOB的JSON,看看能不能数据迁移?
SQL> create table x2 (id int,j clob CONSTRAINT ensure_json2 CHECK (j IS JSON));
表已创建。
SQL> insert into x2 values (1,'{"tel" : "138", "company": "ouyeel", "name" : "张2","address":"宝山"}');
已创建 1 行。
SQL> commit;
提交完成。
SQL> insert into x2 select * from x;
已创建 1 行。
SQL> commit;
提交完成。
SQL> select * from x2 t;
ID
----------
J
--------------------------------------------------------------------------------
1
{"tel" : "138", "company": "ouyeel", "name" : "张2","address
":"宝山"}
1
{"tel" : "137", "company": "ouyeel", "name" : "张","address"
:"宝山"}
实测可以迁移。那么也就是可以字段赋值了。在VARCHAR2的JSON表上再增加一个CLOB的JSON列。
SQL> alter table x add j_clob clob CONSTRAINT ensure_json3 CHECK (j_clob IS JSON);
表已更改。
SQL> update x set j_clob=j;
已更新 1 行。
SQL> commit;
提交完成。
SQL> select * from x t;
ID
----------
J
--------------------------------------------------------------------------------
J_CLOB
--------------------------------------------------------------------------------
1
{"tel" : "137", "company": "ouyeel", "name" : "张","address":"宝山"}
{"tel" : "137", "company": "ouyeel", "name" : "张","address"
:"宝山"}
可以看出,迁移数据没有问题。但是这个做法有点麻烦。其实数据库可以定义VARCHAR2类型到32K。只是这个方法理论上可行,但是实际上不推荐大家做。
因为需要修改数据库参数,这个参数还是静态参数,需要重启生效(这个知识点很少地方介绍到)
SQL> show parameter MAX_STRING_SIZE
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
max_string_size string STANDARD
SQL>
连接到:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.19.0.0.0
修改参数前备份参数,这个很重要。
修改参数前备份参数,这个很重要。
修改参数前备份参数,这个很重要。
SQL> alter system set max_string_size=EXTENDED scope=both;
alter system set max_string_size=EXTENDED scope=both
*
第 1 行出现错误:
ORA-02097: 无法修改参数, 因为指定的值无效 ORA-02095:
无法修改指定的初始化参数
SQL> alter system set max_string_size=EXTENDED scope=spfile;
系统已更改。
SQL> shutdown immediate
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
SQL> startup
ORACLE 例程已经启动。
Total System Global Area 1.0133E+10 bytes
Fixed Size 14416848 bytes
Variable Size 8355053568 bytes
Database Buffers 1744830464 bytes
Redo Buffers 19136512 bytes
数据库装载完毕。
数据库已经打开。
SQL> alter pluggable database all open;
alter pluggable database all open
*
第 1 行出现错误:
ORA-14694: 数据库必须处于 UPGRADE 模式下才能开始 MAX_STRING_SIZE 迁移
修改以后还带了一些问题。
尽管我执行了相关的命令但是最终还是回到了Ora-14694的这里。
SQL> shutdown immediate
数据库已经关闭。
已经卸载数据库。
ORACLE 例程已经关闭。
SQL> startup upgrade;
ORACLE 例程已经启动。
Total System Global Area 1.0133E+10 bytes
Fixed Size 14416848 bytes
Variable Size 8355053568 bytes
Database Buffers 1744830464 bytes
Redo Buffers 19136512 bytes
数据库装载完毕。
数据库已经打开。
SQL> @/u01/app/oracle/product/19.3.0/db/rdbms/admin/utl32k.sql
会话已更改。
会话已更改。
DOC>#######################################################################
DOC>#######################################################################
DOC> The following statement will cause an "ORA-01722: invalid number"
DOC> error if the database has not been opened for UPGRADE.
DOC>
DOC> Perform a "SHUTDOWN ABORT" and
DOC> restart using UPGRADE.
DOC>#######################################################################
DOC>#######################################################################
DOC>#
未选定行
DOC>#######################################################################
DOC>#######################################################################
DOC> The following statement will cause an "ORA-01722: invalid number"
DOC> error if the database does not have compatible >= 12.0.0
DOC>
DOC> Set compatible >= 12.0.0 and retry.
DOC>#######################################################################
DOC>#######################################################################
DOC>#
PL/SQL 过程已成功完成。
会话已更改。
已更新 0 行。
提交完成。
系统已更改。
PL/SQL 过程已成功完成。
提交完成。
系统已更改。
会话已更改。
会话已更改。
表已创建。
表已创建。
表已创建。
表被截断。
已创建 0 行。
PL/SQL 过程已成功完成。
STARTTIME
--------------------------------------------------------------------------------
11/29/2023 10:06:31.825868000
PL/SQL 过程已成功完成。
没有错误。
PL/SQL 过程已成功完成。
会话已更改。
会话已更改。
已创建 0 行。
未选定行
未选定行
DOC>#######################################################################
DOC>#######################################################################
DOC> The following statement will cause an "ORA-01722: invalid number"
DOC> error if we encountered an error while modifying a column to
DOC> account for data type length change as a result of enabling or
DOC> disabling 32k types.
DOC>
DOC> Contact Oracle support for assistance.
DOC>#######################################################################
DOC>#######################################################################
DOC>#
PL/SQL 过程已成功完成。
PL/SQL 过程已成功完成。
提交完成。
程序包已变更。
会话已更改。
尽管这个执行成功,但是最终打开数据库时候还是回到了Ora-14694的这里。虽然这个问题应该是可以解决的,但是我还没去深究怎么处理比较好。
由于修改失败,好在备份了参数,用原来参数启动数据库。所以将VARCHAR2变成32K的方案不推荐正式环境使用。尽管我相信一定是支持的,但是很多文档没说明要什么代价,以及影响。就实际踩坑而言就不建议用32K了。而是采用varchar4000或者clob。这个在上线之前预估一下。