Niedawno programista zadał mi ciekawe pytanie. Pracował nad problemem, w którym wartości liczbowe były przechowywane w tabeli, ale gdy odpytywał tę tabelę w PL/SQL Developer, wyświetlała ona końcowe zera po ostatniej cyfrze. Zastanawiał się, czy to przyczyniło się do problemu, który próbował debugować. Deweloper musiał wiedzieć, czy Oracle przechowuje te końcowe zera.
Moja odpowiedź była taka, że Oracle nie przechowuje końcowych zer. Oracle przechowuje tylko wykładnik i mantysę liczby. Oracle nie dopełnia wartości numerycznej zerami z prawej strony. Deweloper wiedział teraz, że jego problem nie dotyczył danych w bazie danych, ale raczej czegoś, co robiła jego platforma programistyczna.
Ale czy moje stwierdzenie było prawdziwe? Wiele razy składałem oświadczenie o tym, jak Oracle działa wewnętrznie, ale potem musiałem wrócić i zweryfikować moje oświadczenie lub udowodnić, że jest fałszywe, co nieuchronnie prowadzi do prawidłowego stwierdzenia.
Aby przetestować moje oświadczenie, utworzyłem prostą tabelę i wstawiłem do niej dane.
SQL> create table test_tab (val number(38,5)); Table created. SQL> insert into test_tab values (25); 1 row created. SQL> insert into test_tab values (25.0); 1 row created. SQL> insert into test_tab values (25.2); 1 row created. SQL> insert into test_tab values (25.20); 1 row created. SQL> commit; Commit complete. SQL> select * from test_tab; VAL ---------- 25 25 25.2 25.2
W SQL*Plus nie widzimy żadnych końcowych zer, mimo że wyraźnie je dodałem. Wartości 25 i 25.0 oraz 25.2 i 25.20 wyglądają tak samo. Ale może właśnie tak SQL*Plus wyświetla wartości. Zrzućmy więc blok danych, aby zobaczyć, jak dokładnie Oracle przechowuje te wartości.
SQL> select file_id,block_id,blocks 2 from dba_extents where segment_name='TEST_TAB'; FILE_ID BLOCK_ID BLOCKS ---------- ---------- ---------- 6 128 8 SQL> alter system dump datafile 6 block min 128 block max 135; System altered.
Musiałem określić plik i numer bloku dla utworzonego przeze mnie segmentu. Następnie wydałem polecenie zrzucenia zawartości bloków danych do pliku śledzenia. Kiedy zajrzysz do pliku śledzenia, wyszukaj słowo kluczowe „block_row_dump”, a poniżej zobaczysz zawartość tych wierszy w zrzucie:
block_row_dump: tab 0, row 0, @0x1f92 tl: 6 fb: --H-FL-- lb: 0x1 cc: 1 col 0: [ 2] c1 1a tab 0, row 1, @0x1f8c tl: 6 fb: --H-FL-- lb: 0x1 cc: 1 col 0: [ 2] c1 1a tab 0, row 2, @0x1f85 tl: 7 fb: --H-FL-- lb: 0x1 cc: 1 col 0: [ 3] c1 1a 15 tab 0, row 3, @0x1f7e tl: 7 fb: --H-FL-- lb: 0x1 cc: 1 col 0: [ 3] c1 1a 15 end_of_block_dump
Widzimy ze zrzutu bloku, że pierwsza wartość ma długość 2 bajtów i składa się ze znaków szesnastkowych „C1 1A”. Drugi rząd ma dokładnie te same wartości! Jest to ważne, ponieważ weryfikuje moje początkowe twierdzenie, że Oracle nie przechowuje żadnych dodatkowych zer w drugim wierszu tabeli. Gdyby było dodatkowe zero, długość nie wynosiłaby 2 bajtów. W trzecim i czwartym rzędzie widzimy, że wartości szesnastkowe są identyczne, „C1 1A 15”.
Ale bądźmy pewni, że te wartości szesnastkowe odpowiadają naszym danym. W tym celu użyjemy procedury DBMS_STATS.CONVERT_RAW_VALUE.
SQL> set serveroutput on SQL> declare 2 n number; 3 begin 4 dbms_stats.convert_raw_value('C11A',n); 5 dbms_output.put_line(n); 6 end; 7 / 25 PL/SQL procedure successfully completed. SQL> declare 2 n number; 3 begin 4 dbms_stats.convert_raw_value('C11A15',n); 5 dbms_output.put_line(n); 6 end; 7 / 25.2 PL/SQL procedure successfully completed.
Tak więc wartości szesnastkowe „C1 1A” są wewnętrzną (surową) reprezentacją „25”, a „C1 1A 15” to 25,2 zgodnie z naszymi oczekiwaniami.
Morał z tej historii jest taki, że czasami, gdy wydaje Ci się, że wiesz, jak działa Oracle wewnętrznie, nadal będziesz musiał opracować przypadek testowy, aby zweryfikować swoje oświadczenia.