Jeg fik en udvikler til at stille mig et interessant spørgsmål for nylig. Han arbejdede på et problem, hvor numeriske værdier blev gemt i en tabel, men da han forespurgte tabellen i PL/SQL Developer, ville den vise bagende nuller efter det sidste ciffer. Han spekulerede på, om dette bidrog til det problem, han forsøgte at fejlfinde. Udvikleren havde brug for at vide, om Oracle lagrede de efterfølgende nuller.
Mit svar var, at Oracle ikke gemmer efterfølgende nuller. Oracle gemmer kun eksponenten og mantissen af tallet. Oracle højrepuder ikke den numeriske værdi med nuller. Udvikleren vidste nu, at hans problem ikke var med dataene i databasen, men snarere noget, hans udviklingsplatform lavede.
Men var mit udsagn sandt? Mange gange har jeg lavet en erklæring om, hvordan Oracle fungerer internt, men så måtte jeg gå tilbage og validere min erklæring eller bevise, at den var falsk, hvilket uundgåeligt fører til den korrekte erklæring.
For at teste mit udsagn lavede jeg en simpel tabel og indsatte data i den.
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
I SQL*Plus ser vi ingen efterfølgende nuller, selvom jeg eksplicit har tilføjet dem. Værdierne 25 og 25.0 samt 25.2 og 25.20 ser alle ens ud. Men måske er det bare sådan, SQL*Plus viser værdierne. Så lad os dumpe datablokken for at se præcis, hvordan Oracle gemmer disse værdier.
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.
Jeg var nødt til at bestemme filen og bloknummeret for mit segment, jeg oprettede. Jeg udstedte derefter kommandoen til at dumpe indholdet af datablokkene til en sporingsfil. Når du ser i sporingsfilen, søg efter søgeordet "block_row_dump", og du kan se indholdet af disse rækker i dump nedenfor:
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
Vi kan se fra blokdumpen, at den første værdi er 2 byte lang og består af hex-tegnene "C1 1A". Den anden række har de samme nøjagtige værdier! Dette er vigtigt, fordi det bekræfter min første påstand om, at Oracle ikke gemmer nogen ekstra nuller for den anden række i tabellen. Hvis der var et ekstra nul, ville længden ikke være 2 bytes. For tredje og fjerde række kan vi se hex-værdierne er identiske, "C1 1A 15".
Men lad os være sikre på, at disse hex-værdier svarer til vores data. For at gøre det bruger vi proceduren 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.
Så hex-værdierne "C1 1A" er den interne (rå) repræsentation af '25' og "C1 1A 15" er 25,2, som vi ville have forventet.
Moralen i denne historie er, at nogle gange, når du tror, du ved, hvordan Oracle fungerer internt, kan du stadig være nødt til at udtænke en testcase for at validere dine udsagn.