Oracle 12C 新特性之扩展数据类型(extended data type)

介绍了Oracle12C中扩展数据类型的新特性,通过调整max_string_size参数至EXTENDED,使得VARCHAR2等数据类型的大小从4K扩展到32K。文章详细记录了这一过程,并对比了扩展数据类型与LOB数据类型的存储方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Oracle 12C 新特性-扩展数据类型,在12c中,与早期版本相比,诸如VARCHAR2, NAVARCHAR2以及 RAW这些数据类型的大小会从4K以及2K字节扩展至32K字节。只要可能,扩展字符的大小会降低对LOB数据类型的使用。为了启用扩展字符大小,你必须将MAX_STRING_SIZE的初始数据库参数设置为EXTENDED。

实验流程:
1.查看参数max_string_size默认值
SQL> show parameter max_string_size
NAME     TYPE VALUE
------------------------------------ ----------- ------------------------------
max_string_size     stringSTANDARD
说明:12C 参数max_string_size默认值是standard,即不改变varchar2、nvarchar2、和raw数据类型的大小限制,和11g保持一致。
2. 关闭数据库
SQL> shutdown immediate;
3. 以升级模式重启数据库
SQL> startup upgrade;
3. 更改参数: ALTERSYSTEM SET MAX_STRING_SIZE=EXTENDED;
SQL> alter system  set max_string_size=extended scope=spfile;
System altered.
4. 执行 utl32k.sql as sysdba:

SQL> @$ORACLE_HOME/rdbms/admin/utl32k.sql

说明:如果环境是cdb+pdb,那么所有的cdb + pdb$seed + pdb 都需要在 startup upgrade; 状态下跑脚本 @$ORACLE_HOME/rdbms/admin/utl32k.sql 。不然,只扩展完cdb后,pdb不能打开,报ORA-14694: database must in UPGRADE mode to begin MAX_STRING_SIZE migration 。

5. 检查一下有没有产生失效对象
SQL> select count(*) from dba_objects where status<>'VALID';
  COUNT(*)
----------
0
6. 关闭数据库
SQL> shutdown immediate;
7. 以读写模式重启数据库
SQL> startup;
8.检查参数max_string_size , 与失效对象。
SQL> show parameter max_string_size
NAME     TYPE VALUE
------------------------------------ ----------- ------------------------------
max_string_size     stringEXTENDED
SQL> select count(*) from dba_objects where status<>'VALID';
  COUNT(*)
----------
0
9. 测试
SQL> create table t_32k (name varchar2(32000)); 
Table created.
SQL> insert into t_32k values('andy'); 
1 row created.
SQL> select * from t_32k;
NAME
--------------------------------------------------------------------------------
andy

对比LOB数据类型,在ASSM表空间管理中,扩展数据类型的字段以SecureFiles LOB加以存储,而在非ASSM表空间管理中,它们则是以BasciFiles LOB进行存储的。

注意:一旦更改,你就不能再将设置改回STANDARD。

转载于:https://www.cnblogs.com/andy6/p/6844666.html

<think>问题:在Oracle中,当将多个字段拼接成一个字段返回时,如果拼接后的字符串超过Oracle的最大长度限制(如VARCHAR2的4000字节),会报错。如何解决? 解决方案: 1. 使用CLOB类型:将拼接结果转换为CLOB类型,因为CLOB可以存储最大4GB的数据。 2. 使用LISTAGG函数的替代方案:如果使用LISTAGG函数,它在超过4000字节时会报错。我们可以使用XMLAGG或COLLECT函数来避免,并返回CLOB。 3. 在应用程序中分块处理:如果无法在数据库层面解决,可以在应用程序中分多次查询然后拼接。 下面分别给出代码示例: 方法1:使用XMLAGG函数(返回CLOB) 示例:将同一个部门的所有员工姓名拼接成一个字符串(用逗号分隔) ```sql SELECT dept_id, RTRIM(XMLAGG(XMLELEMENT(e, ename || ',') ORDER BY ename).EXTRACT('//text()').GETCLOBVAL(), ',') AS employees FROM emp GROUP BY dept_id; ``` 解释: - XMLAGG:使用XML函数进行聚合,它不会因为字符串过长而报错,因为它返回的是XML类型。 - XMLELEMENT(e, ename || ','):为每个ename创建一个XML元素,并加上逗号。 - .EXTRACT('//text()'):提取所有文本内容(去掉XML标签)。 - .GETCLOBVAL():将结果转换为CLOB类型。 - RTRIM(..., ','):去掉最后一个多余的逗号。 方法2:使用COLLECT函数(需要创建类型和函数,但更高效) 首先,创建一个自定义集合类型(如果不存在): ```sql CREATE OR REPLACE TYPE t_varchar2_tab AS TABLE OF VARCHAR2(4000); / ``` 然后,使用COLLECT函数和TABLE函数结合: ```sql SELECT dept_id, (SELECT LISTAGG(column_value, ',') WITHIN GROUP (ORDER BY ROWNUM) FROM TABLE(CAST(COLLECT(ename) AS t_varchar2_tab))) AS employees FROM emp GROUP BY dept_id; ``` 但是,上面的LISTAGG仍然可能超过4000字节。因此,我们可以使用一个函数将集合转换为CLOB: 创建一个函数将字符串集合拼接成CLOB: ```sql CREATE OR REPLACE FUNCTION concat_clob(p_tab IN t_varchar2_tab, p_delim IN VARCHAR2 DEFAULT ',') RETURN CLOB IS l_result CLOB; BEGIN FOR i IN 1..p_tab.COUNT LOOP IF l_result IS NULL THEN l_result := p_tab(i); ELSE l_result := l_result || p_delim || p_tab(i); END IF; END LOOP; RETURN l_result; END; / ``` 然后使用: ```sql SELECT dept_id, concat_clob(CAST(COLLECT(ename ORDER BY ename) AS t_varchar2_tab)) AS employees FROM emp GROUP BY dept_id; ``` 解释: - COLLECT(ename ORDER BY ename):将每个部门的员工姓名收集到一个集合中(并排序)。 - CAST(... AS t_varchar2_tab):将集合转换为自定义类型。 - concat_clob函数:遍历集合并用分隔符拼接成CLOB。 方法3:在应用程序中处理(例如使用Java) 如果数据库版本较低(如11g以下)且不想创建自定义类型,可以在应用程序中分步获取数据,然后拼接。 例如,在Java中,可以分两次查询: 第一次:获取每个部门的员工数量,以及需要拼接的每个员工姓名的长度,然后计算总长度,如果超过4000,则使用CLOB方式或者分块处理。 或者,直接使用CLOB接收: 在Java中,使用JDBC读取CLOB: ```java // 假设已经获取到结果集 rs String employees = null; Clob clob = rs.getClob("employees"); if (clob != null) { employees = clob.getSubString(1, (int) clob.length()); } ``` 但是,如果数据量极大(超过内存),则需要流式读取。 总结:在Oracle中,当拼接的字符串可能超过4000字节时,优先使用返回CLOB的方法(如XMLAGG或自定义函数)。避免使用LISTAGG,因为它有长度限制。 §§相关问题§§: 1. Oracle中LISTAGG函数的限制是什么?如何绕过这些限制? 2. 在Oracle中,除了XMLAGG和COLLECT,还有哪些方法可以拼接字符串并返回大文本? 3. 在Java应用程序中,如何安全地处理从Oracle返回的CLOB数据? 4. 如何优化大文本拼接的查询性能?</think>### 问题分析 在 Oracle 中,当使用 `LISTAGG`、`WM_CONCAT` 或字符串拼接函数将多个字段值合并为一个字段返回时,如果拼接后的字符串超过 Oracle 的最大长度限制(默认 `VARCHAR2` 为 4000 字节),会报错 **`ORA-01489: result of string concatenation is too long`**。以下是几种解决方案: --- ### 解决方案 1:使用 XMLAGG 替代 LISTAGG(返回 CLOB) `XMLAGG` 函数可以处理大文本并返回 `CLOB` 类型,避免长度限制。 ```sql SELECT department_id, RTRIM( XMLAGG( XMLELEMENT(e, first_name || ' ' || last_name || ', ') ORDER BY employee_id ).EXTRACT('//text()').GETCLOBVAL(), ', ' ) AS employee_list FROM employees GROUP BY department_id; ``` **解释**: - `XMLAGG` 聚合 XML 片段,支持大文本 - `XMLELEMENT(e, ...)` 创建 XML 元素(`e` 是任意标签名) - `.EXTRACT('//text()')` 提取纯文本内容 - `.GETCLOBVAL()` 转换为 CLOB 类型 - `RTRIM(..., ', ')` 移除末尾分隔符 - 结果 `employee_list` 是 CLOB 类型,支持最大 4GB 数据 --- ### 解决方案 2:自定义聚合函数返回 CLOB 创建自定义聚合函数处理大文本拼接: #### 步骤 1:创建类型 ```sql CREATE OR REPLACE TYPE clob_agg_type AS OBJECT ( clob_val CLOB, STATIC FUNCTION ODCIAggregateInitialize( ctx IN OUT clob_agg_type ) RETURN NUMBER, MEMBER FUNCTION ODCIAggregateIterate( self IN OUT clob_agg_type, value IN VARCHAR2 ) RETURN NUMBER, MEMBER FUNCTION ODCIAggregateTerminate( self IN clob_agg_type, returnValue OUT CLOB, flags IN NUMBER ) RETURN NUMBER, MEMBER FUNCTION ODCIAggregateMerge( self IN OUT clob_agg_type, ctx2 IN clob_agg_type ) RETURN NUMBER ); / ``` #### 步骤 2:实现类型方法 ```sql CREATE OR REPLACE TYPE BODY clob_agg_type IS STATIC FUNCTION ODCIAggregateInitialize(ctx IN OUT clob_agg_type) RETURN NUMBER IS BEGIN ctx := clob_agg_type(EMPTY_CLOB()); RETURN ODCIConst.SUCCESS; END; MEMBER FUNCTION ODCIAggregateIterate( self IN OUT clob_agg_type, value IN VARCHAR2 ) RETURN NUMBER IS BEGIN self.clob_val := self.clob_val || value || ', '; RETURN ODCIConst.SUCCESS; END; MEMBER FUNCTION ODCIAggregateTerminate( self IN clob_agg_type, returnValue OUT CLOB, flags IN NUMBER ) RETURN NUMBER IS BEGIN returnValue := RTRIM(self.clob_val, ', '); RETURN ODCIConst.SUCCESS; END; MEMBER FUNCTION ODCIAggregateMerge( self IN OUT clob_agg_type, ctx2 IN clob_agg_type ) RETURN NUMBER IS BEGIN self.clob_val := self.clob_val || ctx2.clob_val; RETURN ODCIConst.SUCCESS; END; END; / ``` #### 步骤 3:创建聚合函数 ```sql CREATE FUNCTION clob_agg(input VARCHAR2) RETURN CLOB PARALLEL_ENABLE AGGREGATE USING clob_agg_type; ``` #### 步骤 4:使用自定义函数 ```sql SELECT department_id, clob_agg(first_name || ' ' || last_name) AS employee_list FROM employees GROUP BY department_id; ``` **优势**: - 完全控制拼接逻辑 - 返回 CLOB 支持超大文本 - 可复用 --- ### 解决方案 3:分块处理(适用于超大数据) 如果数据量极大(如百万行),使用分组分块处理: ```sql WITH data AS ( SELECT department_id, first_name || ' ' || last_name AS name, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY employee_id) AS rn FROM employees ) SELECT department_id, LISTAGG(name, ', ') WITHIN GROUP (ORDER BY rn) AS chunk_result FROM data GROUP BY department_id, TRUNC((rn - 1) / 100) -- 每100行为一组 ORDER BY department_id; ``` **解释**: - 使用 `ROW_NUMBER()` 为每行生成序号 - `TRUNC((rn - 1) / 100)` 将数据分为每组 100 行 - 对每个分块使用 `LISTAGG`(确保每块不超过 4000 字节) - 在应用程序中合并最终结果 --- ### 解决方案 4:启用扩展字符串(Oracle 12c+) 在 Oracle 12c 及以上版本,可修改最大字符串长度: ```sql -- 1. 关闭数据库 SHUTDOWN IMMEDIATE; -- 2. 启动到升级模式 STARTUP UPGRADE; -- 3. 修改参数 ALTER SYSTEM SET MAX_STRING_SIZE = EXTENDED; -- 4. 运行官方脚本 @?/rdbms/admin/utl32k.sql -- 5. 重启数据库 SHUTDOWN IMMEDIATE; STARTUP; ``` **效果**: - `VARCHAR2` 最大长度从 4000 → 32767 字节 - `NVARCHAR2` 从 2000 → 16383 字节 - **注意**:需重启数据库,且不可逆 --- ### 总结建议 | 方案 | 适用场景 | 优点 | 缺点 | |-----------------------|----------------------------------|--------------------------|--------------------| | **XMLAGG** | 快速解决,兼容所有版本 | 无需创建对象 | 语法较复杂 | | **自定义聚合函数** | 需要复用或复杂逻辑 | 灵活可控,性能好 | 需创建数据库对象 | | **分块处理** | 超大数据量(百万行以上) | 避免单次操作过大 | 需应用层合并结果 | | **MAX_STRING_SIZE** | Oracle 12c+,长期解决方案 | 一劳永逸解决长度问题 | 需停机,不可逆操作 | **通用建议**: 1. 优先使用 `XMLAGG` 快速解决问题 2. 需要高性能时创建自定义聚合函数 3. 超大数据量结合分块处理 4. 新项目直接启用 `MAX_STRING_SIZE=EXTENDED`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值