こんにちは。
だいぶ前に調べた内容ですが、メモとして残したいと思います。
時々、DBに登録したレコードを登録順に出力してほしいと言われることがあります。
この対応として、ROWIDでORDER BY
しておきました!という声を耳にしたことがありませんか。
※筆者も駆け出しのとき、間違えていたと記憶しています。
タチが悪いのは、テストしても登録順に並んでいる時もあるんです。。(←言い訳)
結論から言うと、ROWIDでORDER BY
しても登録順に取得できない
です。
そもそも登録順に取得したいという要件が、本当に必須かが怪しいのですが、、それは置いておきます。
あえてやるという事であれば、登録順を保持する列を追加する
必要があります。
ROWID
がなにかを理解してもらえれば、「ああ、全然違うねー
」と分かっていただけるかと思います。
#ROWIDってなあに??#
これに尽きるかと思います。
ROWID
は、どこにレコードの情報があるかというデータの保存場所
の情報を保持しています。
内容は、表領域番号、相対ファイル番号、ブロック番号、スロット番号です。
あの謎の長い文字列はデータの保存場所
をBASE64に変換した文字列
です。
ここからちょっと小難しいので、まとめ
までサラッと流してもいいです。
・BASE64変換
値をアルファベット(a~z,A~Z)と数字(0~9)、一部の記号(+,/)に置き換える。
バイナリデータを6bitずつ取り出し(足りない分は0を追加)、6ビットとAscii文字の変換表を用いて、4文字ずつにする(4文字に満たない場合は=を追加)。
参考: wiki Base64
・ROWIDの形式:OOOOOOFFFBBBBBBSSS
O:表領域番号 (データ長:6)
F:相対ファイル番号 (データ長:3)
B:ブロック番号 (データ長:6)
S:スロット番号 (データ長:3)
ROWIDは、これをBASE64変換して格納しているという事になります。
#おまけ#
ROWIDの中身を見てみたいときは、下記で確認できます。
//BASE64のエンコード/デコード
declare
target varchar2(255);
encoded varchar2(255);
decoded varchar2(255);
begin
-- エンコード対象文字列
target := 'Hello01!';
dbms_output.put_line('target=' || target);
-- base64でエンコード
encoded := utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(target)));
dbms_output.put_line('encoded=' || encoded);
-- base64でデコード
decoded := utl_raw.cast_to_varchar2(utl_encode.base64_decode(utl_raw.cast_to_raw(encoded)));
dbms_output.put_line('decoded=' || decoded);
end;
/
#まとめ#
ROWIDは、データの保存場所の情報です。
登録順でソートするには、登録順を保持する列を追加する必要があります。
以上ですが、ご参考になれば幸いです。