Nedávno mi jeden vývojář položil zajímavou otázku. Pracoval na problému, kdy byly číselné hodnoty uloženy v tabulce, ale když se na tuto tabulku zeptal v PL/SQL Developer, za poslední číslicí se zobrazily koncové nuly. Uvažoval, jestli to nepřispívá k problému, který se snažil odladit. Vývojář potřeboval vědět, zda Oracle ukládá tyto koncové nuly.
Moje odpověď byla, že Oracle neukládá koncové nuly. Oracle ukládá pouze exponent a mantisu čísla. Oracle nevyplňuje číselnou hodnotu vpravo nulami. Vývojář nyní věděl, že jeho problém není s daty v databázi, ale spíše s něčím, co dělá jeho vývojová platforma.
Ale byl můj výrok pravdivý? Mnohokrát jsem učinil prohlášení o tom, jak Oracle interně funguje, ale pak jsem se musel vrátit a potvrdit své prohlášení nebo prokázat, že bylo nepravdivé, což nevyhnutelně vede ke správnému prohlášení.
Abych své tvrzení otestoval, vytvořil jsem jednoduchou tabulku a vložil do ní data.
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
V SQL*Plus nevidíme žádné koncové nuly, i když jsem je explicitně přidal. Hodnoty 25 a 25,0 a také 25,2 a 25,20 vypadají všechny stejně. Ale možná je to jen způsob, jakým SQL*Plus zobrazuje hodnoty. Pojďme tedy vypsat datový blok, abychom viděli, jak přesně Oracle tyto hodnoty ukládá.
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.
Musel jsem určit soubor a číslo bloku pro svůj segment, který jsem vytvořil. Poté jsem vydal příkaz k výpisu obsahu datových bloků do trasovacího souboru. Když se podíváte do trasovacího souboru, vyhledejte klíčové slovo „block_row_dump“ a níže uvidíte obsah těchto řádků ve výpisu:
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
Z výpisu bloku vidíme, že první hodnota je dlouhá 2 bajty a skládá se z hexadecimálních znaků „C1 1A“. Druhý řádek má stejné přesné hodnoty! To je důležité, protože to potvrzuje mé původní tvrzení, že Oracle neukládá žádné nuly navíc pro druhý řádek v tabulce. Pokud by tam byla nula navíc, délka by nebyla 2 bajty. U třetího a čtvrtého řádku vidíme, že hexadecimální hodnoty jsou identické, „C1 1A 15“.
Ale buďme si jisti, že tyto hexadecimální hodnoty odpovídají našim datům. K tomu použijeme postup 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.
Hexadecimální hodnoty „C1 1A“ jsou tedy vnitřní (nezpracované) vyjádření „25“ a „C1 1A 15“ je 25,2, jak bychom očekávali.
Morálka tohoto příběhu spočívá v tom, že někdy, když si myslíte, že víte, jak Oracle interně funguje, možná budete muset vymyslet testovací případ pro ověření vašich prohlášení.