Recentemente, um desenvolvedor me fez uma pergunta interessante. Ele estava trabalhando em um problema em que valores numéricos eram armazenados em uma tabela, mas quando consultava essa tabela no PL/SQL Developer, ela mostrava zeros à direita após o último dígito. Ele se perguntou se isso estava contribuindo para o problema que ele estava tentando depurar. O desenvolvedor precisava saber se a Oracle estava armazenando esses zeros à direita.
Minha resposta foi que o Oracle não armazena zeros à direita. O Oracle armazena apenas o expoente e a mantissa do número. O Oracle não preenche o valor numérico com zeros à direita. O desenvolvedor agora sabia que seu problema não era com os dados no banco de dados, mas com algo que sua plataforma de desenvolvimento estava fazendo.
Mas minha afirmação era verdadeira? Muitas vezes eu fiz uma declaração sobre como a Oracle funciona internamente, mas depois tive que voltar e validar minha declaração ou provar que era falsa, o que inevitavelmente leva à declaração correta.
Para testar minha declaração, criei uma tabela simples e inseri dados nela.
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
No SQL*Plus, não vemos zeros à direita, embora eu os tenha adicionado explicitamente. Os valores 25 e 25.0, bem como 25.2 e 25.20, parecem todos iguais. Mas talvez seja assim que o SQL*Plus está exibindo os valores. Então, vamos despejar o bloco de dados para ver como exatamente o Oracle está armazenando esses valores.
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.
Eu tive que determinar o arquivo e o número do bloco para o meu segmento que criei. Em seguida, emiti o comando para despejar o conteúdo dos blocos de dados em um arquivo de rastreamento. Quando você olhar no arquivo de rastreamento, procure a palavra-chave “block_row_dump” e você poderá ver o conteúdo dessas linhas no dump abaixo:
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
Podemos ver no dump do bloco que o primeiro valor tem 2 bytes e consiste nos caracteres hexadecimais “C1 1A”. A segunda linha tem os mesmos valores exatos! Isso é importante porque verifica minha afirmação inicial de que o Oracle não está armazenando zeros extras para a segunda linha da tabela. Se houvesse um zero extra, o comprimento não seria de 2 bytes. Para a terceira e quarta linha, podemos ver que os valores hexadecimais são idênticos, “C1 1A 15”.
Mas vamos ter certeza de que esses valores hexadecimais correspondem aos nossos dados. Para fazer isso, usaremos o procedimento 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.
Portanto, os valores hexadecimais "C1 1A" são a representação interna (bruta) de '25' e "C1 1A 15" é 25,2 como esperávamos.
A moral desta história é que, às vezes, quando você pensa que sabe como a Oracle está trabalhando internamente, ainda pode ter que criar um caso de teste para validar suas declarações.