Oracle 19c对VARCHAR2的限制

Oracle每个新的版本都会带来一些新的特性,或者对现有功能的扩展,例如传统的VARCHAR2数据类型支持4000字节,从12c开始,就可以扩展长度(可参考《支持超过4000字节的varchar2类型》)。

JiekeXu老师的这篇文章《Oracle 19c 新特性|增加 VARCHAR2 数据类型的大小限制》讲解了19c中对VARCHAR2数据类型的扩展,而且得到了大牛的推荐,值得学习。

Tim Hall大师(https://oracle-base.com/misc/site-info#biog)将来自全球社区伙伴的博客汇总到一起(https://oracle-base.com/blog/),JiekeXu老师的这篇《Increased Size Limit for VARCHAR2, NVARCHAR2, and RAW Data Types#JoelKallmanDay》文章排在第六位。

链接:https://xiaoqiangxu.wordpress.com/2022/10/11/increased-size-limit-for-varchar2-nvarchar2-and-raw-data-typesjoelkallmanday

e2a8f9fbf484054b5f46d1223a57aa94.png

下面我打算在原有英文的基础上修改修改,翻译回中文,使读起来更顺畅,排版更美观。

On the Oracle New Features Website, there is a new feature about 
extending data types“The maximum size of the VARCHAR2, NVARCHAR2, 
and RAW data types has been increased from 4,000 to 32,767 bytes“.

在Oracle新特性网站上(https://apex.oracle.com/database-features/),有一个关于扩展数据类型的新特性“VARCHAR2、NVARCHAR2和RAW数据类型的最大大小已从4000增加到32,767字节”。

ac6af7de90c0845eed3510551f06a827.png

我认为它很有趣,很喜欢,也很实用,所以我想介绍它。

以Varchar2数据类型为例,

The VARCHAR2 data type specifies a variable-length character string 
in the database character set. You specify the database character 
set when you create your database.

VARCHAR2数据类型在数据库字符集中指定一个可变长度的字符串。在创建数据库时指定数据库字符集。

When you create a table with a VARCHAR2 column, you must specify 
the column length as size optionally followed by a length qualifier. The 
qualifier BYTE denotes byte length semantics while the qualifier 
CHAR denotes character length semantics. In the byte length semantics, size 
is the maximum number of bytes that can be stored in the column. In 
the character length semantics, size is the maximum number of code 
points in the database character set that can be stored in the 
column. A code point may have from 1 to 4 bytes depending on the 
database character set and the particular character encoded by the 
code point. Oracle recommends that you specify one of the length 
qualifiers to explicitly document the desired length semantics 
of the column. If you do not specify a qualifier, the value of the 
NLS_LENGTH_SEMANTICS parameter of the session creating the column 
defines the length semantics, unless the table belongs to the schema 
SYS, in which case the default semantics is BYTE.

当创建带有VARCHAR2列的表时,必须将列长度指定为可选的大小,后面跟着一个长度限定符。限定符BYTE表示字节长度语义,而限定符CHAR表示字符长度语义。在字节长度语义中,size是列中可以存储的最大字节数。在字符长度语义中,size是数据库字符集中可以存储在列中的代码点的最大数量。一个代码点可能有1到4个字节,这取决于数据库字符集和代码点编码的特定字符。Oracle建议您指定一个长度限定符来显式记录列所需的长度语义。如果不指定限定符,则创建列的会话的NLS_LENGTH_SEMANTICS参数的值定义长度语义,除非表属于模式SYS,在这种情况下默认语义为BYTE。

Oracle stores a character value in a VARCHAR2 column exactly as you 
specify it, without any blank-padding, provided the value does not 
exceed the length of the column. If you try to insert a value that 
exceeds the specified length, then Oracle returns an error ORA-12899.

Oracle在VARCHAR2列中存储的字符值与您指定的完全一致,没有任何空白填充,只要该值不超过列的长度。如果试图插入一个超过指定长度的值,Oracle将返回一个ORA-12899:value too large for column “SYS”.“T5”.“E” (actual: 4000, maximum: 3999)错误。

9ab9e0358b46d6689725fa19781fcb23.png

However, in the Oracle 19c official documentation description,
The minimum value of size is 1. The maximum value is
  • 32767 bytes if MAX_STRING_SIZE = EXTENDED

  • 4000  bytes if MAX_STRING_SIZE = STANDARD

但是在Oracle 19c官方文档中,size的最小值是 1。最大值为4000或者32767是需要根据参数MAX_STRING_SIZE的值确定,当值为EXTENDED时,size大小才可以为32767。

While size may be expressed in bytes or characters (code points) the 
independent absolute maximum length of any character value that can be 
stored into a VARCHAR2 column is 32767 or 4000 bytes, depending on 
MAX_STRING_SIZE. For example, even if you define the column length to be
32767 characters, Oracle returns an error if you try to insert a 
32767-character value in which one or more code points are wider than 1 
byte. The value of size in characters is a length constraint, not 
guaranteed capacity. If you want a VARCHAR2 column to be always able to 
store size characters in any database character set, use a value of size
that is less than or equal to 8191, if MAX_STRING_SIZE = EXTENDED, 
or 1000, if MAX_STRING_SIZE = STANDARD.

虽然大小可以用字节或字符(码位)表示,但可以存储到VARCHAR2列中的任何字符值的独立绝对最大长度是32767或4000字节,这取决于MAX_STRING_SIZE。例如,即使您将列长度定义为32767个字符,如果您试图插入一个32767个字符的值,其中一个或多个编码点的宽度大于1字节,Oracle也会返回一个错误。字符大小的值是长度限制,而不是容量保证。如果希望VARCHAR2列始终能够在任何数据库字符集中存储大小字符,如果 MAX_STRING_SIZE = EXTENDED,则使用小于或等于8191的size值,如果 MAX_STRING_SIZE = STANDARD,则使用小于或等于1000的size值。

However, I found that the MAX_STRING_SIZE parameter does not need to 
be set to EXTENDED to create the varchar2 extension data type and 
insert the data successfully.The following is my test in 
each version of the database. Isn’t it interesting that there is 
a discrepancy with the official Oracle documentation?

但是,我发现不需要将MAX_STRING_SIZE参数设置为EXTENDED来创建varchar2扩展数据类型,并成功插入数据。以下是我在每个版本的数据库中的测试。与Oracle官方文档描述的有点差异,这是不是很有趣呢?

DB Version 19.15

d1f0f706f529537ff5b58d74b001f238.png

DB Version 19.15

bbd5bfb8b4203126fe04a6805a36699c.png

DB Version 19.4

sqlplus / as sysdba
set timing on time on


SQL*Plus: Release 19.0.0.0.0 - Production on Mon Oct 10 13:43:15 2022
Version 19.4.0.0.0
Copyright (c) 1982, 2019, Oracle. All rights reserved.
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.4.0.0.0


show parameter max_string_size
NAME TYPE VALUE
max_string_size string STANDARD


14:02:42 SYS@bisal> create table T1(C clob, E varchar2(32737));
Table created.


14:03:09 SYS@bisal> insert into t1 select lpad(rownum,32767,'X'),lpad(rownum,32767,'X') from dual connect by level <=10;
10 rows created.


14:03:14 SYS@bisal> commit;
Commit complete.


14:03:19 SYS@bisal> set autot trace stat


14:04:04 SYS@bisal> select C from t1;

DB Version 19.3

cfebb2765f79137d95ff3fe6ff4cf2d3.png

DB Version 12.2

444f39f859d18bbdc282612f403c2e0a.png

Because I don’t have the database versions of Oracle 12.1.0.1 and 
12.0.1.2 for the moment, I can’t test it. I wonder whether these 
two versions need to set parameters and restart the database.
Interested friends with the environment can test whether the 
following results are correct.

因为我当前没有Oracle 12.1.0.1和12.0.1.2的数据库版本,所以我不能测试它。我想知道这两个版本是否需要设置参数并重新启动数据库。对12.1环境感兴趣的朋友可以测试下是否正确,是否需要修改参数并跑utl32k.sql脚本。

16bf4029b87955eef0feae5dde92c43f.png

So what’s the problem? You know what? Give me a homework assignment.

那么问题是什么呢?你知道吗?继续往下看,后面会揭秘。

So what are the benefits of Extended Data types?

那么扩展数据类型的好处是什么呢?

Increasing the allotted size for these data types allows users to 
store more information in character data types before switching to 
large objects (LOBs). This is especially useful for brief textual 
data types and the capabilities to build indexes on these types 
of columns And Extended VARCHAR2 reduces the number of network 
round-trips.

增加这些数据类型的分配大小允许用户在切换到大对象(lob)之前在字符数据类型中存储更多的信息。这对于简短的文本数据类型以及在这些类型的列上构建索引的功能尤其有用。Extended VARCHAR2减少了网络往返的数量。

980e1f6d7fed9c2979236c96fc01c9e7.png

Update on Day 2

Today, my friend sent me a screenshot of version 12.1.0.2.The result is a direct error in the SQL construct sentence.

今天,我的朋友给我发了一张12.1.0.2版本的运行截图。结果是SQL语句在建表时就抛出了一个数据类型太长的错误。

f5d4ebf73386ea5e915974346c894ed3.png

In the test process of version 12.2, it is found that no matter how 
large the size is, it can be successfully created.So why is this 
happening? Let’s move on.

在12.2版本的测试过程中发现,无论size大小有多大,表都可以成功创建,而且数据也会插入成功。那么,为什么会出现这种情况呢?让我们继续。

7d69ccbb4b698f3fdedf98b007edc7ef.png

Now for the reveal开始揭秘

在我的一台单机的19.12的环境下,创建表进行测试。

create table T1(id int, E varchar2(32767));
CREATE SEQUENCE SEQ_T1 MINVALUE 1 MAXVALUE 99999999999999 INCREMENT BY 1 START WITH 1 CACHE 50 ORDER NOCYCLE;
insert into t1 values(SEQ_T1.nextval,lpad('x',32767,'x'));
insert into t1 values(SEQ_T1.nextval,lpad('x',32768,'x'));
commit;
select length(E) from t1;
And you can see that whether I use 32767 or 32768 I’m going to end up
with a length of 4000.So the official document says there is no 
error, we have to modify the parameter MAX_STRING_SIZE = EXTENDED .

你可以看到,无论我用 32767 还是 32768,最终长度都是 4000。所以官方文档说没有错误,我们必须修改参数 MAX_STRING_SIZE = EXTENDED。

a192a39503b18ceea94866befb375af7.png

(database must in UPGRADE mode)

数据库需要startup UPGRADE以升级模式启动,然后才能修改参数,并且需要跑脚本utl32k.sql,然后关闭数据库再正常重启数据库。

453e2f879ab2db7d2ae8b1dddf3ec1a1.png

Therefore, it can be seen from the above that under normal 
circumstances, the maximum value of varchar2 can only be 4000, 
and the excess part will be truncated. After the database is 
started in upgrade mode, utl32k.sql is run, and parameters are 
modified, vatchar2 can store up to 32767 bytes, and the excess 
bytes are truncated.

因此,由上表可以看出,在正常情况下,varchar2的最大值只能为4000,多余的部分会被截断。数据库以升级模式启动后,修改参数,跑utl32k.sql脚本再正常重启数据库。varchar2最多可以存储32767个字节,多余的字节被截断。顺便说一句,NVARCHAR2和RAW在这场景下最多也可以存储32767个字节。

值得注意的是如果您的数据库是RAC,需要关闭node2并修改集群参数CLUSTER_DATABASE=FALSE。如果你的数据库还有ADG备库,请立即修改MAX_STRING_SIZE并重启,否则备库不修改此参数会导致MRP0进程异常宕掉,造成主备数据不同步的情况。

node2: shu immediate


node1:
alter system set CLUSTER_DATABASE=FALSE scope=spfile;
alter system set MAX_STRING_SIZE = EXTENDED scope=spfile;
shu immediate
startup upgrade
@?/rdbms/admin/utl32k.sql


alter system set CLUSTER_DATABASE=TRUE scope=spfile;
shu immediate
startup
show parameter max_string_size
create table T1(E varchar2(32767));
insert into t1 values(lpad(‘x’,32767,’x’));
commit;
select length(E) from t1;


node2: startup


----ADG instance
alter system set CLUSTER_DATABASE=TRUE scope=spfile;
shu immediate
startup
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT;

扩展数据类型

从Oracle Database 12c开始,您可以为VARCHAR2、NVARCHAR2和RAW数据类型指定最大大小为32767字节。可以通过设置初始化参数MAX_STRING_SIZE来控制您的数据库是否支持这个新的最大大小,如下所示:

  • 如果MAX_STRING_SIZE = STANDARD,则适用于Oracle Database 12c之前的版本的大小限制:VARCHAR2和NVARCHAR2数据类型为4000字节,RAW数据类型为2000字节。这是默认设置。

  • 如果MAX_STRING_SIZE = EXTENDED,则VARCHAR2、NVARCHAR2和RAW数据类型的大小限制为32767字节。

声明大小大于4000字节的VARCHAR2或NVARCHAR2数据类型,或声明大小大于2000字节的RAW数据类型,都是扩展数据类型。扩展的数据类型列是在线存储的,利用Oracle的LOB技术。LOB存储始终与表对齐。在使用自动段空间管理(ASSM)管理的表空间中,扩展的数据类型列存储为SecureFiles lob。否则,它们被存储为BasicFiles lob。lob作为存储机制的使用只是内部的。因此,您不能使用DBMS_LOB包操作这些lob。Oracle强烈反对使用BasicFiles lob作为存储机制。BasicFiles lob不仅对扩展数据类型列的功能施加了限制,而且计划在未来的版本中弃用BasicFiles数据类型。

注意,尽管必须设置MAX_STRING_SIZE = EXTENDED才能将RAW数据类型的大小设置为大于2000 字节,但只有当RAW数据类型的大小大于4000字节时,它才被存储为out-of-line LOB。例如,为了声明RAW(3000)数据类型,必须设置MAX_STRING_SIZE = EXTENDED。但是,列是内联存储的。

注意事项:

您可以像使用标准数据类型一样使用扩展数据类型,需要注意以下事项:

1、如果列是扩展数据类型列,则在尝试创建索引时可能会收到“超出最大键长度”错误。索引的最大键长度取决于数据库块大小和存储在块中的一些附加索引元数据。例如,对于使用Oracle标准8K块大小的数据库,最大密钥长度约为6400字节。

要解决这种情况,您必须使用以下方法之一缩短要索引的值的长度:

  • 创建基于函数的索引以缩短存储在扩展数据类型列中的值,作为用于索引定义的表达式的一部分。

  • 创建虚拟列以缩短存储在扩展数据类型列中的值,作为用于虚拟列定义的表达式的一部分,并在虚拟列上构建普通索引。使用虚拟列还使您能够利用常规列的功能,例如收集统计信息以及使用约束和触发器。

对于这两种方法,您都可以使用SUBSTR或者STANDARD_HASH函数来缩短扩展数据类型列的值以构建索引。这些方法具有以下优点和缺点:

  • 使用该SUBSTR函数返回一个子字符串或前缀,column它是索引键可接受的长度。这种类型的索引可用于原始列上的相等、IN-list和范围谓词,而无需将SUBSTR列指定为谓词的一部分。

  • 使用该STANDARD_HASH函数可能会创建一个比基于子字符串的索引更紧凑的索引,并且可能会减少不必要的索引访问。这种类型的索引可用于原始列上的相等和IN-list谓词,而无需将STANDARD_HASH列指定为谓词的一部分。

以下示例显示如何在扩展数据类型列上创建基于函数的索引:

CREATE INDEX index ON table (SUBSTR(column, 1, n));


---- 对于 n,指定一个足够大的前缀长度以区分列中的值。

以下示例显示如何为扩展数据类型列创建虚拟列,然后在虚拟列上创建索引:

ALTER TABLE table ADD (new_hash_column AS (STANDARD_HASH(column)));
CREATE INDEX index ON table (new_hash_column);

2、如果列表分区的分区键列是扩展数据类型列,则希望为分区指定的值列表可能超过分区边界的4K字节限制。有关如何解决此问题的信息,请参阅create table的list_partitions子句。

3、初始化参数的值MAX_STRING_SIZE会影响以下内容:

  • 文本文字的最大长度。有关详细信息,请参阅文本字面量。

  • 连接两个字符串的大小限制。有关详细信息,请参阅串联运算符。

  • NLSSORT函数返回的排序规则键的长度。请参阅NLSSORT。

  • 对象的某些属性的大小XMLFormat。有关详细信息,请参阅XML格式模型。

  • 以下XML函数中某些表达式的大小:XMLCOLATTVAL、XMLELEMENT、XMLFOREST、XMLPI和XMLTABLE。

如果您认为这篇文章有些帮助,还请不吝点下文章末尾的"点赞"和"在看",或者直接转发pyq,

63f756ab35d5b80adfe32c8c8d650b6e.png

近期更新的文章:

提供使用国密算法的MySQL

MySQL中timestamp数据类型定义

魔方的征途 - 魔方如何选择?

MySQL中用户密码存在特殊字符的使用场景

企业IT运维故障定位方法及工具

近期的热文:

"红警"游戏开源代码带给我们的震撼

文章分类和索引:

公众号1100篇文章数据统计

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值