CHAR czy VARCHAR2 oto jest pytanie




Weekend spędziłam bardzo miło prowadząc blok PL/SQL na warsztatach SQL Workshops organizowanych przez Geek Girls Carrots. 

Weekend z Oracle to coś co lubię!

Podczas jednej z dyskusji zostałam zapytana, dlaczego w przygotowanym specjalnie na warsztaty schemacie bazy danych używam typu danych VARCHAR2 zamiast CHAR. Padały sugestie, że CHAR, jako że ma stałą długość, jest wydajniejszy, głównie pod kątem alokacji pamięci. 

Czy tak naprawdę jest? Czym w zasadzie różni się typ CHAR od VARCHAR2? I czemu VARCHARa nie po prostu VARCHAR?

Czym w takim razie różni się CHAR od VARCHAR2? 
Można by odpowiedzieć, że niczym. Lub, że wszystkim :)

Miejsce na dysku
CHAR jest to typ tekstowy o stałej długości znaków. Jeśli w polu typu CHAR(50) zapiszemy wartość o długości 2 znaków - Oracle uzupełni wartość do 50 znaków spacjami z prawej strony. Czyli jeśli zadeklarujemy kolumnę CHAR(50) - a zapiszemy w kolumnie tylko dwa znaki - i tak dane zajmą 52 byte'y (50 byte'ów długość danych, 2 byte'e informacja o długości pola). 

Dane typu VARCHAR2 z kolei zajmują na dysku tyle miejsca, ile zajmują dane, niezależnie, od tego ile znaków zadeklarowaliśmy. Czy jeśli mamy zmienną VARCHAR2(50) ale w kolumnie zapiszemy tylko 2 znaki - na dysku zajmiemy nie 52 byte'y, jak byłoby w przypadku CHAR, ale tylko 4 byte'y.

Limity długości
CHAR maksymalnie może mieć 2000 znaków.
VARCHAR2 maksymalnie pomieści 4000 znaków a nawet, od wersji 12 (po drobnym administracyjnych sztuczkach) - 32767 znaków. 

Alokacja pamięci
I tu jest nawet ciekawe. Otóż CHAR i VARCHAR2 - pobierane są do pamięci przez Oracle w dokładnie taki sam sposób - a co więcej - alokują dokładnie taką samą ilość pamięci dla długości pola < 2000 - czyli maksymalną możliwą długość dla pola CHAR - 2000 byte'ów. Nawet jeśli w polu typu VARCHAR2 są dane o krótszej długości niż 2000 znaków. 

Dla danych o długości > 2000 znaków - alokowana jest taka ilość pamięci - jaką ma długość wartość pola.

Także przy alokacji pamięci nie ma znaczenia czy używamy CHAR czy VARCHAR2 (jeśli mówimy o danych o długości < 2000 znaków).
Za to VARCHAR2 zajmuje mniej miejsca na dysku, jeśli wartości są mniejsze niż maksymalna zadeklarowana długość pola.

Ale to nie duża zajętość miejsca na dysku dyskwalifikuje CHAR. 
Najgorsze w używaniu CHAR jest to, że dane w kolumnie NIE SĄ takimi, jak je ustawiliśmy. Oracle do wartości pola dokleja spacje z prawej strony, aż do pełnej zadeklarowanej długości.

A spacji nie widać na pierwszy rzut oka. 'A ' nie równa się 'A'.

Może to prowadzić do błędnego działania aplikacji, błędnego wyszukiwania danych, problemów z porównywaniem wartości czy nawet do problemów z wydajnością! 

Żeby obsłużyć typ CHAR, będziemy potrzebowali przy porównywaniach pól pozbyć się tych wszystkich nadmiarowych spacji. A to prowadzi do konieczności "ubrania" kolumny w funkcję co pozbawia nas możliwości korzystania z indexów (no chyba że sprytnie założymy index funkcyjny).

Sprawdźmy na przykładzie.

Utwórzmy tabelę z dwoma kolumna, jedna CHAR(10), druga VARCHAR2(10). Dodajmy do niej rekord. Obie kolumny ustawimy na tę samą wartość A.

 create table tabA (kol_char char(10), kol_vchar varchar2(10));  
 insert into tabA values ('A', 'A');  

Przetestujmy wyszukiwanie danych:
Dla kolumny VARCHAR2

 select * from tabA where kol_vchar = 'A';  







Dla kolumny CHAR:

 select * from tabA where kol_char = 'A';  







Wynik wygląda tak samo... Ale spróbujmy jeszcze tak:

 select * from tabA where kol_vchar = kol_char  

I tu już niespodzianka:

 no data found  

Dlaczego?
Przy wyszukiwaniu po wartości pola 'A' - Oracle wykonał niejawną konwersję i "obciął" nadmiarowe spacje w kolumnie typu CHAR. A pamiętajmy, że niejawna konwersja to killer dla naszych indexów!

Jednak w przypadku porównania dwóch kolumn - kol_char i kol_varchar - Oracle nie wykonuje niejawnej konwersji i nie usuwa spacji. A że wartości są różne - różnią się właśnie o te spacje - Oracle nie znajdzie nam żadnego pasującego rekordu.

Także pomimo, że dla kolumn o stałej długości np. flagi Y/N itp  - czy użyjemy typu CHAR czy typ VARCHAR2 - nie ma wielkiego znaczenia, jednak rekomenduję używanie ZAWSZE  typu VARCHAR2. Problemy, jakie nam może stworzyć używanie CHAR są na tyle poważne, że najlepiej po prostu zapomnieć  o tym typie danych.

Także cytując Tom'a Kyte'a
Just say NO TO CHAR.

AskTOM

ps. A co z typem VARCHAR - zapytają dociekliwi? Jemy też powiedz NIE! Obecnie (do wersji Oracle 12 włącznie) VARCHAR i VARCHAR2 zachowują się i działają tak samo. W przyszłości jednak impelmentacja VARCHAR może ulec zmianie. VARCHAR2  - nie będzie modyfikowane. Także żeby nie było niespodzianek - najlepiej trzymać się starego, dobrego VARCHAR2.

Komentarze

  1. Wygląda na to, że jednak constant-size rows pozwalają się zabezpieczyć przed spowolnieniami opisanymi tutaj https://www.akadia.com/services/ora_chained_rows.html. Oczywiście nie ma co stosować na siłę stałych rozmiarów, jednak jak wiemy, że wartość ma mieć zawsze X znaków użycie CHAR'a może mieć sens.

    OdpowiedzUsuń
    Odpowiedzi
    1. Cześć! Dzięki za komentarz.
      Chained rows/migrated są spowodowane sytuacjami, gdy rekord nie mieści się w bloku lub gdy tabela ma więcej niż 255 kolumn. Jeśli na tabeli jest sporo updatów - należy dostosować parametry PCTFREE, PCTUSED (choć obecnie odchodzi się od ręcznego sterowania tymi parametrami. CHAR to jest nic innego w ORACLE jak VARCHAR2 - uzupełniony z prawej strony spacjami.
      W Oracle powinniśmy ZAWSZE używać VARCHAR2.

      Usuń
  2. 44 yr old Data Coordiator Jamal Croote, hailing from Port Hawkesbury enjoys watching movies like Colossal Youth (Juventude Em Marcha) and Graffiti. Took a trip to Teide National Park and drives a Maserati A6G/2000 Spyder. mozesz sprobowac tez z

    OdpowiedzUsuń
  3. Ten komentarz został usunięty przez administratora bloga.

    OdpowiedzUsuń

Prześlij komentarz