修复 Oracle 4000 字符中 char 允许的最大长度
Posted
技术标签:
【中文标题】修复 Oracle 4000 字符中 char 允许的最大长度【英文标题】:Fix for Maximum length allowed for char in Oracle 4000 character 【发布时间】:2019-02-02 01:16:58 【问题描述】:我目前正在使用 Oracle 11g。根据我的要求,我需要将前 4000 个(最大限制)字符存储到我的变量中(用于触发器)。
为此,我正在使用 VarName = SUBSTR 函数(VarName, 1, 4000),但它似乎没有存储任何内容(我正在传递 '111...'(要存储的 4096 个字符))仅存储 ('111...') 前 4000 个字符)。
它适用于 4 个字符,但不适用于最大限制。我尝试将长度设置为 3999、3000,但没有任何效果。
请对此进行调查并建议我一些解决方案。
(*注意:我无法将变量类型从 varchar 更改为 clob,因为该列已经有数百万的旧数据。)
*修改:我尝试使用 Substr 长度 2048,它工作但是一旦我向 substr 提供 2049 个字符,它又变成了 null强>。 但在数据类型字段值定义为 VARCHAR2(4000 CHAR)。
【问题讨论】:
可以在将 X 插入数据库之前修剪它吗?一般来说,您应该避免由 DB 完成的操作,因为您希望 DB 速度快。此外,如果您有缓存层,则需要在 DB 和缓存处修剪 X。 你的数据库的编码是什么?你字段是 4k 字节还是 4k 字符? 当我超过 1000 个字符栏时,我通常会开始考虑使用CLOB
s。
@vipero07:修剪将帮助我删除如果我知道从哪里修剪和修剪什么的空白,因为任何东西都可以进入那个变量 X 所以不是修剪,我是使用 Substr.
请用表格和触发器组合一个脚本来演示发生了什么。然后将其发布在 livesql.oracle.com 上,我(和其他人)可以看看问题出在哪里。当然,简单地将大小从 4 更改为 4000 不会导致返回空值。如果一开始有 4000 个空格,它会返回那些,所以我认为修剪不是问题。
【参考方案1】:
如果我是对的,你最好升级到 oracle 12。让我解释一下:
Oracle(包括最高版本 11)对于 char/varchar 列的硬限制为 4000 BYTES,并且不能通过简单地将列声明为“varchar2( 4000 个字符)”.
如果您的数据库使用多字节字符集,则该限制仍然存在,因此只有满足这两个条件,您才能实际存储 4000 个字符:
-
在你的字符集中有只占一个字节的字符
您要存储的字符串仅由编码为单个字节的字符组成
例如:如果你的数据库字符集是UTF8,“A”字符占用一个字节,而“à”字符占用两个字节:
| Bytes required |Bytes required | Bytes required
string | if database charset |if database charset | if database charset
| is ASCII |is UTF8 | is UTF16
--------- |-------------------------------------------------------------------
"A" | 1 byte | 1 bytes | 2 bytes
"à" | 1 byte | 2 bytes | 2 bytes
"àA" | 2 byte | 3 bytes | 4 bytes
所以在 VARCHAR2(4000 CHAR) 列中,在 UTF8 数据库中,您将能够存储由 4000 个“A”字符组成的字符串,但如果您使用 4000 个“à”字符,它将被限制为 2000 个字符:在 UTF8 中,任何代码 unicode 编号 >= 128(即:它不是 ASCII 字符)的字符都需要多个字节。在 UTF16 中 any 字符至少占用 2 个字节。
通过查看您所描述的症状(以及您的姓名),我认为您没有使用单字节字符集...而且我还认为您使用的大多数字符至少需要两个字节,所以这是 2000 个字符实际限制的原因:您面临 4000 个字节的硬限制。
我认为对您来说唯一“简单”的解决方案是将数据库升级到 oracle 12,其中这个硬限制可以提高到 32000 字节(AFAIK 它不会“开箱即用”:您需要在数据库创建期间进行相应的设置)。
如果您使用提高的限制创建 oracle 12 数据库,您的 varchar2(4000 CHAR) 列将不再满足硬限制。
还请记住,当您通常将列声明为 VARCHAR2(4000) 时,oracle 默认将其解释为 VARCHAR2(4000 BYTE)。这个默认值可以通过在你的会话中执行来改变:
alter session set NLS_LENGTH_SEMANTICS='CHAR'
它可以在实例级别为整个数据库和所有会话设置,但是 oracle 警告您不要这样做,因为系统会出现异常,所以请坚持在会话级别使用它。
希望这会有所帮助。
附:也许你会发现这个提示也很有用:
在我的数据库中,我编写了一个“登录后”触发器,它会自动执行,只为我的应用程序的用户更改会话
CREATE OR REPLACE TRIGGER TRG_MYAPP_AFTER_LOGON
AFTER LOGON on database
declare
function IsMyAppUser return boolean is
cnt number;
begin
-- if the user is the owner of the schema, all its commands are expected
-- to use the "char" length semantics
if user = 'MYAPP' then
return true;
end if;
--- this is specific to my app: I have a table that lists the login names
--- of the users who have been created only with the purpose of using the app
select count(*) into cnt
from my_app_users u
where u.usr_login=user
and ROWNUM=1;
return cnt>0;
end;
BEGIN
if IsMyAppUser then
execute immediate 'alter session set nls_length_semantics = ''CHAR''';
end if;
END TRG_QBF_AFTER_LOGON;
【讨论】:
【参考方案2】:
RPAD
或 LPAD
:
我可以像这样获得一个 4000 个字符的单元格:
select rpad('x',3999, '-'), v.*
from v$version v
where rownum=1
给予
|x------[3999 times] | Oracle Database 11g Release 11.2.0.4.0 - 64bit dev|
【讨论】:
fyi:用于进行单行查询的标准表,在 oracle 中被命名为“dual”。不要依赖 v$version。 oracle 将访问“dual”识别为不是真正的表查询,并完全在内存中执行它们而不访问其他数据结构(执行计划显示类似“FAST DUAL”的查询,例如“select sysdate from dual” @CarloSirna,是的,当然。重点只是显示我正在使用的版本... 无论如何:我认为他的问题是我在回答中描述的问题:他使用的是多字节字符集....所以没有 4000 个字符的字符串(至少在他的语言中) using) 可以编码为 4000 字节 可能是对的。我同意这里有点跑题了。以上是关于修复 Oracle 4000 字符中 char 允许的最大长度的主要内容,如果未能解决你的问题,请参考以下文章