修复 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 个字符栏时,我通常会开始考虑使用CLOBs。 @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】:

RPADLPAD:

我可以像这样获得一个 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 允许的最大长度的主要内容,如果未能解决你的问题,请参考以下文章

oracle中如何将long型的数据转换为char型

oracle 中clob内容怎么查询

在 Oracle (> 11g) 中从逗号分隔列表创建表 - 输入字符串限制 4000 个字符

oracle(数据类型)

3表的管理

Oracle常用数据类型