2017-08-22 36 views
1

我有一个NoteDetail表,NoteText的varchar(4000)字段和NoteNumber的number(4,0)。这个想法是,如果一个超过4000个字符的音符进来,它会被分成多个音符,并带有递增的NoteNumber条目。ORA-01461:如果输入字符串> 4000个字符,SUBSTR()是否返回LONG?

执行插入操作的逻辑如下,它在Oracle 10上运行得非常漂亮。最近该应用程序被移动到了Oracle 12c,并且出现错误:“ORA-01461:只能将LONG值绑定为插入一个LONG列“Oracle DBA无法弄清楚为什么会发生这种情况。

下面是我的插入代码的本质;我最好的猜测是,如果传入的字符串足够长,那么即使在分配给varchar2(4000)类型的变量时,SUBSTR()函数也会返回一个长整型。供参考:Oracle SQL Developer指出在插入点(在下面的else块的循环中)发生错误。

有谁知道如何解决这个问题?

DECLARE 
    s_incoming_string varchar2(32000); 
    s_substring_value varchar2(4000); 
    i_note_iteration number(4,0); 
    i_note_iterations number(4,0); 
    i_substr_start  number(6,0); 
    k_NoteId   number(19,0); 
BEGIN 
    SELECT noteid_seq.nextval INTO k_NoteId FROM dual; 
    s_incoming_string := {a 7000 character long note}; 
    i_note_iterations := ceil(length(s_incoming_string)/4000); 

    IF i_note_iteration = i_note_iterations THEN 
    --I NEVER GET AN ERROR ON THIS BRANCH!!! 
    INSERT INTO NoteDetail (NoteId, NoteNumber, NoteText) 
    VALUES (k_NoteId, 1, s_incoming_string); 
    ELSE 
    FOR i_note_iteration IN 1..i_note_iterations 
    LOOP 
      i_substr_start := (4000 * (i_note_iteration - 1)) + 1; 
       IF i_note_iteration = i_note_iterations THEN 
        --this is the last chunk of text; no need 
        --to read past the end of the buffer 
        s_substring_value = SUBSTR(s_incoming_string, i_substr_start); 
       ELSE 
        --I ONLY GET AN ERROR if this branch is executed before the insert: 
        s_substring_value = SUBSTR(s_incoming_string, 4000); 
       END IF; 

     INSERT INTO NoteDetail (NoteId, NoteNumber, NoteText) 
     VALUES (k_NoteId, i_note_iteration, s_incoming_string); 
    END LOOP; 
    END IF; 
END; 

表模式是:

DCLARE TABLE NoteDetail (
     NoteId  number(19,0), 
     NoteNumber number(4,0), 
     NoteText varchar2(4000) 
    ); 
+0

请包括重现问题所需的最短代码。请在表格中追加结构。现在只有你的注释,而不是真正的代码和'insert'语句。 – krokodilko

+0

我添加了模式和更多的代码。 –

+0

对于投票结束这件事的人们,你至少会解释为什么?我现在只是在职业生涯中第一次遇到甲骨文,我不得不让这些样本变得如此通用,所以我不会因泄露公司信息而被解雇。请在这里休息一下。 –

回答

1

如果您的文本列最大为4000字节,请尝试使用SUBSTRB和LENGTHB函数。你也必须小心将它拼回去。

基本上归结为多字节字符编码。如果您使用的是utf8,并且您的文本包含多字节字符,那么4000个字符将超过4000个字节(这是您的表列中的最大值)。您可以像krokodilko建议的那样做,并将其更改为4000个字符,或将数据拆分为4000个字节的块(这是substrb和lengthb函数的作用)。

请注意,根据您希望显示数据的方式,如果您的截止值恰好为4000个字节,则可能在4000字节块的末尾存在多字节字符问题。如果你先将大块拼到一起,它应该没问题,但请确认这个篱笆箱是否合格。明天我会做一个快速测试。

+0

当我开始对LENGTH()和LENGTHB()进行dbms_output.put_line()比较时,它变得很明显发生了什么。现在我看到还有一个LENGTH4()函数......至少我现在知道存在多种charset类型并可能影响我的代码。在SQL Server中没有这种复杂性:只有varchar用于简单的ASCII文本,而nvarchar用于unicode。 –

1

这不是一个准确的回答你的问题,而是一些实验我想展现给你。
的My Oracle 12c中安装有相同的设置你:

SELECT * FROM NLS_DATABASE_PARAMETERS 
WHERE parameter = 'NLS_LENGTH_SEMANTICS' 
    OR parameter like '%SET%' 

PARAMETER     VALUE   
-------------------------- ------------ 
NLS_NCHAR_CHARACTERSET  AL16UTF16  
NLS_CHARACTERSET   AL32UTF8 
NLS_LENGTH_SEMANTICS  BYTE 

现在研究这个代码:

DECLARE 
    s_incoming_string varchar2(32000); 
    s_substring_value varchar2(4000); 
BEGIN 
    LOOP 
     s_incoming_string := s_incoming_string || 'żaba'; 
     exit when length(s_incoming_string) > 4500; 
    END LOOP; 
    DBMS_OUTPUT.put_line('Length in characters = ' || length(s_incoming_string)); 
    DBMS_OUTPUT.put_line('Length in bytes = ' || lengthb(s_incoming_string)); 

    s_substring_value := substr(s_incoming_string, 1, 4000); 

    DBMS_OUTPUT.put_line('Length in characters = ' || length(s_substring_value)); 
    DBMS_OUTPUT.put_line('Length in bytes = ' || lengthb(s_substring_value)); 
END; 
/

如果我运行上面的代码,我得到以下的输出:

Length in characters = 4504 
Length in bytes = 5630 

Error report - 
ORA-06502: PL/SQL: numeric or value error: character string buffer too small 
ORA-06512: at line 12 
06502. 00000 - "PL/SQL: numeric or value error%s" 
*Cause: An arithmetic, numeric, string, conversion, or constraint error 
      occurred. For example, this error occurs if an attempt is made to 
      assign the value NULL to a variable declared NOT NULL, or if an 
      attempt is made to assign an integer larger than 99 to a variable 
      declared NUMBER(2). 
*Action: Change the data, how it is manipulated, or how it is declared so 
      that values do not violate constraints. 

现在如果我更改的声明到varchar2(4000 char);代码工作没有任何错误,它提供了以下结果:

DECLARE 
    s_incoming_string varchar2(32000); 
    s_substring_value varchar2(4000 char); 
BEGIN 
    LOOP 
     s_incoming_string := s_incoming_string || 'żaba'; 
     exit when length(s_incoming_string) > 4500; 
    END LOOP; 
    DBMS_OUTPUT.put_line('Length in characters = ' || length(s_incoming_string)); 
    DBMS_OUTPUT.put_line('Length in bytes = ' || lengthb(s_incoming_string)); 

    s_substring_value := substr(s_incoming_string, 1, 4000); 

    DBMS_OUTPUT.put_line('Length in characters = ' || length(s_substring_value)); 
    DBMS_OUTPUT.put_line('Length in bytes = ' || lengthb(s_substring_value)); 

END; 
/

Length in characters = 4504 
Length in bytes = 5630 
Length in characters = 4000 
Length in bytes = 5000 

PL/SQL procedure successfully completed. 

更多关于这个问题,你可以在这里找到:NLS_LENGTH_SEMANTIC


注:波兰语字符ż是编码作为UTF8中的两个字节;

select dump('ż') as x from dual; 
X      
--------------------- 
Typ=96 Len=2: 197,188 

因此字符串:żaba(英文:青蛙)需要5个字节,而不是4


注2:你也需要改变表的定义:

ALTER TABLE NoteDetail 
MODIFY NoteText varchar2(4000 char); 
相关问题