Ik had onlangs een ontwikkelaar die me een interessante vraag stelde. Hij werkte aan een probleem waarbij numerieke waarden in een tabel werden opgeslagen, maar toen hij die tabel opvroeg in PL/SQL Developer, zou hij nullen laten zien na het laatste cijfer. Hij vroeg zich af of dit bijdroeg aan het probleem dat hij probeerde op te lossen. De ontwikkelaar moest weten of Oracle die nullen opsloeg.
Mijn antwoord was dat Oracle geen volgnullen opslaat. Oracle slaat alleen de exponent en mantisse van het getal op. Oracle vult de numerieke waarde niet met nullen naar rechts. De ontwikkelaar wist nu dat zijn probleem niet bij de gegevens in de database lag, maar bij iets dat zijn ontwikkelplatform aan het doen was.
Maar was mijn bewering waar? Vaak heb ik een verklaring afgelegd over hoe Oracle intern werkt, maar toen moest ik teruggaan en mijn verklaring valideren of bewijzen dat deze onjuist was, wat onvermijdelijk tot de juiste verklaring leidt.
Om mijn verklaring te testen, heb ik een eenvoudige tabel gemaakt en daarin gegevens ingevoegd.
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
In SQL*Plus zien we geen volgnullen, hoewel ik ze expliciet heb toegevoegd. De waarden 25 en 25.0 evenals 25.2 en 25.20 zien er allemaal hetzelfde uit. Maar misschien is dit precies hoe SQL*Plus de waarden weergeeft. Dus laten we het datablok dumpen om te zien hoe Oracle deze waarden precies opslaat.
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.
Ik moest het bestands- en bloknummer bepalen voor mijn segment dat ik had gemaakt. Ik gaf toen de opdracht om de inhoud van de gegevensblokken naar een traceerbestand te dumpen. Wanneer u in het traceerbestand kijkt, zoekt u naar het trefwoord "block_row_dump" en u kunt de inhoud van deze rijen in de onderstaande dump zien:
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
We kunnen aan de blokdump zien dat de eerste waarde 2 bytes lang is en bestaat uit de hexadecimale tekens "C1 1A". De tweede rij heeft dezelfde exacte waarden! Dit is belangrijk omdat het mijn aanvankelijke bewering bevestigt dat Oracle geen extra nullen opslaat voor de tweede rij in de tabel. Als er een extra nul zou zijn, zou de lengte niet 2 bytes zijn. Voor de derde en vierde rij kunnen we zien dat de hexadecimale waarden identiek zijn, "C1 1A 15".
Maar laten we er zeker van zijn dat deze hexadecimale waarden overeenkomen met onze gegevens. Om dat te doen, gebruiken we de procedure 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.
Dus de hexadecimale waarden "C1 1A" zijn de interne (ruwe) weergave van '25' en "C1 1A 15" is 25,2 zoals we hadden verwacht.
De moraal van dit verhaal is dat je soms, als je denkt te weten hoe Oracle intern werkt, toch een testcase moet bedenken om je uitspraken te valideren.