2016-07-06 15 views
0

ich eine benutzerdefinierte STRAGG bin mit, die Zeichenfolge bei 4000 abschneidet, wie folgend,Pufferüberlauf-Fehler auf individuelle STRAGG Funktion in Oracle

select STRAGG('000 - ' || CHANGE_TEXT) from TBL_CHANGE_SUMMARY

dies in der Entwicklung von Oracle-Server funktioniert gut, jedoch wird es in der Produktion folgende Fehler Umgebung.

ORA-06502: PL/SQL: numeric or value error: character string buffer too small

Es folgt der benutzerdefinierten Code, der STRAGG,

geändert hatte anders
CREATE OR REPLACE TYPE "STRING_AGG_TYPE" as object 
(
    total varchar2(4000), 
    static function ODCIAggregateInitialize(sctx IN OUT string_agg_type) return number, 
    member function ODCIAggregateIterate(self IN OUT string_agg_type, 
             value IN varchar2) return number, 
    member function ODCIAggregateTerminate(self  IN string_agg_type, 
             returnValue OUT varchar2, 
             flags  IN number) return number, 
    member function ODCIAggregateMerge(self IN OUT string_agg_type, 
            ctx2 IN string_agg_type) return number 
); 

CREATE OR REPLACE TYPE BODY "STRING_AGG_TYPE" is 

    static function ODCIAggregateInitialize(sctx IN OUT string_agg_type) return number is 
    begin 
    sctx := string_agg_type(null); 

    return ODCIConst.Success; 
    end; 

    member function ODCIAggregateIterate(self IN OUT string_agg_type, 
             value IN varchar2) return number is 
    BEGIN 
     --prevent buffer overflow for more than 4,000 characters 
    IF NVL(LENGTH(self.total), 0) + NVL(LENGTH(VALUE), 0) < 3930 THEN 
     IF (self.total IS NULL) THEN 
      self.total := VALUE; 
     ELSIF INSTR ('~#~' || self.total || '~#~', '~#~' || VALUE || '~#~',1,1) = 0 AND INSTR (self.total, '[TRUNCATED]',1,1) = 0 THEN 
       self.total := self.total || '~#~' || VALUE; 
     END IF; 
    ELSE 
     IF INSTR (self.total, '[TRUNCATED]',1,1) = 0 THEN 
      self.total := self.total || '[TRUNCATED]'; 
     END IF; 
    END IF; 

     RETURN ODCIConst.Success; 
    END; 


    MEMBER FUNCTION ODCIAggregateTerminate (self   IN  string_agg_type, 
              returnValue  OUT VARCHAR2, 
              flags   IN  NUMBER) 
     RETURN NUMBER 
    IS 
    BEGIN 
     IF INSTR (self.total, '[TRUNCATED]',1,1) = 0 THEN 
     returnValue := LTRIM (self.total, '~#~'); 
     ELSE 
     IF (self.total IS NOT NULL) THEN 

      returnValue := SUBSTR(self.total, 1, INSTR (self.total, '[TRUNCATED]',1,1) - 1) || '..... Truncated because of system limitation'; 
     END IF; 
     END IF; 

     RETURN ODCIConst.Success; 
    END; 


    member function ODCIAggregateMerge(self IN OUT string_agg_type, 
            ctx2 IN string_agg_type) return number is 
    begin 
    self.total := self.total || ctx2.total; 
    return ODCIConst.Success; 
    end; 


end; 

Warum sollte denselben Code verhalten? Gibt es irgendetwas, das ich in der Oracle-Umgebung ändern oder einrichten muss?

Nach ist der Unterschied in der Anwendung von Zeichen in jeder Umgebung,

Character set in Production, 
NLS_CHARACTERSET   AL32UTF8 
NLS_NCHAR_CHARACTERSET UTF8 
NLS_LENGTH_SEMANTICS  BYTE 

Character set in Development, 
NLS_CHARACTERSET  AL32UTF8 
NLS_NCHAR_CHARACTERSET AL16UTF16 
NLS_LENGTH_SEMANTICS BYTE 
+0

Nun, was Linie ist der Fehler an? Sie müssen unterschiedliche Daten in prod haben, nein? – OldProgrammer

+0

Welches Zeichensatz verwenden Sie? Könnten Sie diese Abfrage ausführen: 'Wählen Sie * aus nls_database_parameters wo Parameter wie '% SEMA%' oder Parameter wie '% SET';' und fügen Sie das Ergebnis an die Frage? – krokodilko

+0

Zeile des Fehlers wäre nicht in STRING_AGG_TYPE, aber würde in tatsächlichen SQL sein, die gleiche Zeile wie mein erstes Beispiel ist "wählen Sie STRAGG ('000 -' || CHANGE_TEXT) von ...". Und ja, es ist andere Daten in der Produktion, aber ich denke nicht, es ist die Daten, wie ich viele Szenarien der Datengröße untersucht und es funktioniert immer in der Entwicklungsumgebung. – yangkwak

Antwort

0

Sie AL32UTF8 verwenden - in UTF-8 einige Zeichen codiert wird unter Verwendung von 1 Byte, während andere 2 Bytes oder manchmal 4 Byte belegen.

Sie verwenden auch NLS_LENGTH_SEMANTICS = BYTE (das ist eine Standardeinstellung).
Dies bedeutet, dass eine Deklaration wie VARCHAR2(5) eine Länge einer Zeichenfolge
auf ein Maximum von 5 Bytes, nicht 5 Zeichen beschränkt.

Verwenden VARCHAR2(4000 char), nicht einfach VARCHAR2(4000)

ein Beispiel sehen Sie, wie es funktioniert:

select * 
from nls_database_parameters 
where parameter like '%SEMA%' 
    or parameter like '%SET'; 
PARAMETER     VALUE    
------------------------- -------------------- 
NLS_LENGTH_SEMANTICS  BYTE     
NLS_NCHAR_CHARACTERSET UTF8     
NLS_CHARACTERSET   AL32UTF8 

und jetzt:

declare 
    x varchar2(10); 
begin 
    x := 'ąśćz123456'; 
    dbms_output.put_line(x); 
end; 
/
ORA-06502: PL/SQL: błąd liczby lub wartości: character string buffer too small 
ORA-06512: przy linia 4 
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. 

aber:

declare 
    x varchar2(10 char); 
begin 
    x := 'ąśćz123456'; 
    dbms_output.put_line(x); 
end; 
/

ąśćz123456 

PL/SQL procedure successfully completed. 
+0

Vielen Dank für die ausführliche Erklärung! Eine kurze Frage, also ist das erfolgreiche Beispiel unten AL16UTF16? Und wenn ja, sollte NLS_CHARACTERSET mit NLS_NCHAR_CHRACTERSET übereinstimmen? – yangkwak

+0

Kurz gesagt: 'VARCHAR2' verwenden' NLS_CHARACTERSE' und 'NVARCHAR2' verwenden' NLS_NCHAR_CHARACTERSET'. Dies ist ein großes Thema, bitte lesen Sie die Dokumentation für eine detaillierte Erklärung: https://docs.oracle.com/cd/E11882_01/server.112/e10729/ch2charset.htm#NLSPG002 – krokodilko