最近、開発者に興味深い質問をしてもらいました。彼は数値が表に格納されている問題に取り組んでいましたが、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」を検索すると、以下のダンプでこれらの行の内容を確認できます。
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バイト長で、16進文字「C11A」で構成されていることがわかります。 2番目の行はまったく同じ値です!これは、Oracleがテーブルの2番目の行に余分なゼロを格納していないという私の最初の主張を検証するために重要です。余分なゼロがあった場合、長さは2バイトにはなりません。 3行目と4行目では、16進値が同じ「C11A15」であることがわかります。
ただし、これらの16進値がデータに対応していることを確認しましょう。そのためには、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.
したがって、16進値「C11A」は「25」の内部(生)表現であり、「C11A15」は予想どおり25.2です。
この話の教訓は、Oracleが内部でどのように機能しているかを知っていると思う場合でも、ステートメントを検証するためのテストケースを考案しなければならない場合があるということです。