BYTE czy CHAR czyli dlaczego 4 jest mniejsze od 4.


Deklarujesz zmienną o długości 4. Do zmiennej przypisujesz tekst o długości 4 znaki. Uruchamiasz procedurę i ..... dostajesz błąd:

ORA-06502: PL/SQL: błąd liczby lub wartości: za mały bufor tekstowy

Nasuwa się pytanie: dlaczego?

Zerknijmy na kod:

declare
    v varchar2(4);
begin

    v:= 'ąbćź'; 
    
    DBMS_OUTPUT.PUT_LINE (V);
end;
/

No niby się zgadza. Zadeklarowana maksymalna długość zmiennej to 4 i do zmiennej też przypisujemy 4 znaki. Powinno działać, czyż nie?

Długość zmiennej typu VARCHAR2 można zadeklarować jako BYTE lub CHAR.

BYTE  - oznacza, że maksymalna długość naszej zmiennej może zajmować dokładnie tyle bajtów, ile zadeklarowaliśmy. BYTE'ów! A pamiętajmy, że niektóre znaki, na przykład polskie ogonki nie mieszczą się na jednym bajcie!

select length('c'), lengthb('c'),
       length('ć'), lengthb('ć')
from dual;

LENGTH('C') LENGTHB('C') LENGTH('Ć') LENGTHB('Ć')
----------- ------------ ----------- ------------
          1            1           1            2


Na literkę c wystarczy jeden bajt ale już ć wymaga dwóch bajtów.

CHAR -  deklarując rozmiar zmiennej jako CHAR mówimy bazie, że zmienna ma przechowywać tyle znaków, ile zadeklarowaliśmy BEZ WZGLĘDU na to, ile zajmują bajtów. Dzięki czemu możemy mieć pewność (PRAWIE), że nasze polskie ogonki się zmieszczą w zmiennej.

Czemu prawie? Maksymalna długość zmiennej typu varchar2 to 32767 bajty. Bez względu, czy zadeklarowaliśmy rozmiar zmiennej jako CHAR czy jako BYTE. Więc jeśli zmienna jest zadeklarowana jako VARCHAR2(32767 CHAR) i nie przekroczymy ilość znaków ale przekroczymy ilość potrzebnych bajtów to i tak dostaniemy błąd 06502...

declare
    v varchar2(32767 char);
begin
    v := lpad(' V', 32767, 'ć');

end;
/

ORA-06502: PL/SQL: błąd liczby lub wartości: za mały bufor tekstowy


No dobrze, ale patrząc na nasz przykład - zmienna jest zadeklarowana bez wskazywania czy jest to CHAR czy BYTE. Można tak?

Domyślnie zmienna jest deklarowana zgodnie z parametrem nls_length_semantics.
Możemy sprawdzić wartość parametru w widoku v$parameter

SELECT
    name,
    value
FROM
    v$parameter
WHERE
    name = lower('NLS_LENGTH_SEMANTICS');


nls_length_semantics    BYTE


Możemy oczywiści modyfikować ten parametr tak dla systemu jak i dla sesji:

alter system set NLS_LENGTH_SEMANTICS = 'CHAR';
alter session set NLS_LENGTH_SEMANTICS = 'CHAR';

To by wyjaśniało błąd z przykładu  - domyślnie na systemie jest ustawiony rozmiar zmiennych varchar2 jako BYTE a my próbowaliśmy przypisać do zmiennej o rozmiarze 4 bajtów - ciąg znaków zajmujących 7 bajtów. No wiadomo, że się nie zmieści :)

Sprawdźmy na przykładzie:

 declare
 vByte varchar(4 BYTE);
 vChar varchar(4 CHAR);
 begin
    begin
        vByte:= 'ąbćź';
        DBMS_OUTPUT.PUT_LINE('vByte: ' || vByte);
    exception when others then
        DBMS_OUTPUT.PUT_LINE ('vByte: ' || sqlerrm);
    end;
    
    DBMS_OUTPUT.new_line;
    
    begin
         vChar:= 'ąbćź';
         DBMS_OUTPUT.PUT_LINE('vChar: ' || vChar);
    exception when others then
        DBMS_OUTPUT.PUT_LINE ('Blad dla vChar ' || sqlerrm);
    end;

 end;
 /
PL/SQL procedure successfully completed.

vByte: ORA-06502: PL/SQL: błąd liczby lub wartości: za mały bufor tekstowy

vChar: ąbćź

Ten same tekst przypisany do zmiennych o tym samym rozmiarze ale w innych "jednostkach" rozmiaru. Raz działa a raz nie działa. Cuda :)

Na koniec jeszcze przykład dlaczego to działa PRAWIE  zawsze :)

declare
 vByte varchar(32767 BYTE);
 vChar varchar(32767 CHAR);
 begin
    begin
        vByte:= lpad(' B', 32767, 'ź');
        DBMS_OUTPUT.PUT_LINE('vByte: ' || vByte);
    exception when others then
        DBMS_OUTPUT.PUT_LINE ('vByte: ' || sqlerrm);
    end;
    
    DBMS_OUTPUT.new_line;
    
    begin
         vChar:= lpad(' C', 32767, 'ź');
         DBMS_OUTPUT.PUT_LINE('vChar: ' || vChar);
    exception when others then
        DBMS_OUTPUT.PUT_LINE ('vChar: ' || sqlerrm);
    end;

 end;
 /
    

vByte: ORA-06502: PL/SQL: błąd liczby lub wartości: za mały bufor tekstowy

vChar: ORA-06502: PL/SQL: błąd liczby lub wartości: za mały bufor tekstowy

Także 32767 ź nie mieści się ani w zmiennej o maksymalnym rozmiarze w BYTE'ach - to już wiemy ale nie mieści się również w zmiennej o maksymalnym rozmiarze w CHAR'ach.

Także pamiętajmy, że do zmiennej o długości 4 wcale nie muszą się zmieścić 4 znaki!

Piszę tylko o zmiennych typu VARCHAR2 pomijając typy CHAR i VARCHAR z uwagi na CHAR czy VARCHAR2 oto jest pytanie oraz ze względu na fakt, że zachowują się tak jak VARCHAR2. Także ten...

Komentarze