Наскоро накарах разработчик да ми зададе интересен въпрос. Той работеше върху проблем, при който числовите стойности бяха съхранени в таблица, но когато поиска тази таблица в PL/SQL Developer, тя щеше да покаже крайни нули след последната цифра. Той се чудеше дали това допринася за проблема, който се опитваше да отстрани. Разработчикът трябваше да знае дали Oracle съхранява тези последващи нули.
Моят отговор беше, че Oracle не съхранява последващи нули. Oracle съхранява само степента и мантисата на числото. Oracle не поставя вдясно числовата стойност с нули. Разработчикът вече знаеше, че проблемът му не е в данните в базата данни, а по-скоро в нещо, което неговата платформа за разработка прави.
Но вярно ли беше моето твърдение? Много пъти съм правил изявление за това как Oracle работи вътрешно, но след това трябваше да се върна и да потвърдя твърдението си или да докажа, че е невярно, което неизбежно води до правилното твърдение.
За да тествам изявлението си, създадох проста таблица и вмъкнах данни в нея.
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
В SQL*Plus не виждаме никакви нули в края, въпреки че ги добавих изрично. Стойностите 25 и 25.0, както и 25.2 и 25.20 изглеждат еднакви. Но може би точно така SQL*Plus показва стойностите. Така че нека изхвърлим блока от данни, за да видим как точно Oracle съхранява тези стойности.
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.
Трябваше да определя номера на файла и блока за моя сегмент, който създадох. След това издадох командата за изхвърляне на съдържанието на блоковете данни във файл за проследяване. Когато погледнете файла за проследяване, потърсете ключовата дума „block_row_dump“ и можете да видите съдържанието на тези редове в dump по-долу:
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
От дъмпа на блока можем да видим, че първата стойност е дълга 2 байта и се състои от шестнадесетичните символи „C1 1A“. Вторият ред има същите точни стойности! Това е важно, защото потвърждава първоначалното ми твърдение, че Oracle не съхранява никакви допълнителни нули за втория ред в таблицата. Ако имаше допълнителна нула, дължината нямаше да е 2 байта. За третия и четвъртия ред можем да видим, че шестнадесетичните стойности са идентични, „C1 1A 15“.
Но нека бъдем сигурни, че тези шестнадесетични стойности съответстват на нашите данни. За да направим това, ще използваме процедурата 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.
Така че шестнадесетичните стойности „C1 1A“ са вътрешното (необработено) представяне на „25“, а „C1 1A 15“ е 25,2, както бихме очаквали.
Моралът на тази история е, че понякога, когато си мислите, че знаете как Oracle работи вътрешно, може да се наложи да измислите тестов случай, за да потвърдите твърденията си.