Oracle 4000字符中char类型的最大长度修复方案

4

我目前正在使用Oracle 11g。根据我的要求,我需要将前4000个(最大限制)字符存储到我的变量中(用于触发器)。

为此,我正在使用VarName = SUBSTR函数(VarName,1,4000),但似乎它什么也没有存储(我传递了'111...'(4096个字符需要存储),其中我只想要存储('111...')的前4000个字符)。

对于4个字符可以工作,但是对于最大限制不起作用。我尝试将长度设置为3999、3000,但对我都无效。

请你查看一下并为我提供解决方案。

(*注意:我不能将变量类型从varchar更改为clob,因为该列已经有数百万条遗留数据。)

*修改:我尝试使用substr长度2048,它可以工作,但是一旦我提供2049个字符给substr,它又变成空值了。 但在数据类型字段值中定义为VARCHAR2(4000 CHAR)


你能在将X插入数据库之前对其进行修剪吗?通常情况下,应避免由数据库执行的操作,因为您希望数据库运行快速。此外,如果您有缓存层,则需要在数据库和缓存中都对X进行修剪。 - vipero07
你的数据库采用什么编码?你的字段是4K字节还是4K字符? - hotfix
当字符数超过1000个时,我通常会考虑使用“CLOB”。 - The Impaler
@vipero07:修剪功能可以帮助我去除字符串中的空格,只要我知道从哪里开始修剪和修剪什么内容。因为变量X可能包含任何内容,所以我使用Substr而不是Trim。 - Sumit Suman
2
请编写一个包含表格和触发器的脚本,以演示正在发生的情况。然后将其发布在livesql.oracle.com上,我(和其他人)可以查看并确定问题所在。当然,仅仅将大小从4更改为4000不会导致返回null。如果开头有4000个空格,它将返回这些空格,因此我认为修剪不是问题所在。 - Steven Feuerstein
显示剩余3条评论
2个回答

5
如果我没错的话,你最好升级到Oracle 12。让我解释一下:
Oracle(包括11版本)对char/varchar列有严格的限制,最大只能存储4000个字节。即使把列声明为“varchar2(4000 CHAR)”,也无法突破这一限制。
如果你的数据库使用了多字节字符集,仍然会受到这一限制,因此只有在满足以下两个条件的情况下,你才能真正地存储4000个字符:
  1. in your charset there are characters that occupy only one byte
  2. the string you want to store is made up only using the characters that are encoded to a single byte

    For example: if your database character set is UTF8, the "A" character occupies one byte, but the "à" character needs two bytes:

                |  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       
    
在一个UTF8数据库内的VARCHAR2(4000 CHAR)列中,你可以存储由4000个“A”字符组成的字符串,但如果你使用了由4000个“à”字符组成的字符串,则会限制为2000个字符:在UTF8中,任何代码Unicode编号>= 128(即它不是ASCII字符)的字符都需要多个字节。在UTF16中,任何字符至少占用2个字节。
通过描述的症状(以及您的姓名),我认为您并没有使用单字节字符集……而且我也认为您使用的大多数字符至少需要两个字节,因此这里是实际限制为2000个字符的原因:您面临着4000个字节的硬限制。
我认为你唯一“简单”的解决方案是将数据库升级到Oracle 12,在那里可以将这个硬限制提高到32000字节(据我所知,它不是“开箱即用”的:您需要在数据库创建期间相应地设置它)。
如果你创建了一个带有提高的限制的Oracle 12数据库,你的varchar2(4000 CHAR)列将不再遇到硬限制。
还要记住,当你通常将一个列声明为VARCHAR2(4000)时,Oracle默认将其解释为VARCHAR2(4000 BYTE)。这个默认值可以通过在你的会话中执行以下命令来更改:
 alter session set NLS_LENGTH_SEMANTICS='CHAR'

理论上,可以在实例级别上为整个数据库和所有会话设置,但是Oracle警告您不要这样做,因为系统会表现异常,因此请坚持在会话级别使用。

希望这可以帮到您。


P.S. 也许您还会发现这个提示有用:

在我的数据库中,我编写了一个“登录后”触发器,自动执行仅针对我的应用程序用户的alter session。

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;

0

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”)。 - Carlo Sirna
@CarloSirna,是的,当然。我的意思只是想展示我正在使用的版本... - J. Chomel
无论如何:我认为他的问题就是我在答案中描述的那个问题:他正在使用多字节字符集……因此,在他使用的语言中,不存在一个可以编码为4000字节的4000个字符的字符串。 - Carlo Sirna
可能是对的。我有点跑题了,我同意。 - J. Chomel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接