DBD::Oracle に激遅のコードが混在している件について

えーっと、ここ数日やっている DBIx::Class 関連なんですが、DBIx::Class::Schema::Loader::Oracle を作ってみたものの、Oracle の場合、なぜか _load_relationships が異常に遅いんですよ。でかなり深追いしてみました。

DBIx::Class::Schema::Loader::Base->_load_relationships あたりが遅いのでメソッドコールを深追い
 ↓
DBIx::Class::Storage::DBI->columns_info_for の $dbh->column_info そのものが遅いことを突き止める。

eval {
  my $sth = $dbh->column_info( undef, undef, $table, '%' );
  $sth->execute();
  while ( my $info = $sth->fetchrow_hashref() ){
  ・・・

$dbh->column_info つまりは、DBD::Oracle->column_info が原因って事で、まさかの DBD::Oracle を除いてみることに。

- スポンサーリンク -

DBD::Oracle の解析

DBD::Oracle にデバッグ情報をいれつつ、生成された SQL をみてみた。。。 LIKE 文がすごーく非効率的につかわれてます。まずは結論。修正した Oracle.pm との差分は下記の通り。
diff Oracle.pm Oracle.pm.org 
371,372c371,372
<               if ( defined $SchVal && $SchVal ne '%' ) {
<                       push @Where, ($SchVal =~ /%/) ? "TABLE_SCHEM LIKE '$SchVal' ESCAPE '\\'" : "TABLE_SCHEM = '$SchVal'";
---
>               if ( defined $SchVal ) {
>                       push @Where, "TABLE_SCHEM LIKE '$SchVal' ESCAPE '\\'";
374,375c374,375
<               if ( defined $TblVal && $TblVal ne '%' ) {
<                       push @Where, ($TblVal =~ /%/) ? "TABLE_NAME  LIKE '$TblVal' ESCAPE '\\'" : "TABLE_NAME  = '$TblVal'";
---
>               if ( defined $TblVal ) {
>                       push @Where, "TABLE_NAME  LIKE '$TblVal' ESCAPE '\\'";
621,622c621,622
<           if( $v && $v ne '%' ) {
<               $Sql .= ($v =~ /%/) ? "   AND $k LIKE ? ESCAPE '\\'\n" : "   AND $k = ?\n";
---
>           if ( $v ) {
>               $Sql .= "   AND $k LIKE ? ESCAPE '\\'\n";
627d626
< warn "DBD::Oracle->@BindVals\n$Sql\n";

で、説明。まずオリジナルのコードは条件文をすべて LIKE 文で生成しようとします。たとえば、

TABLE_NAME = 'TEST_TABLE' と出力して欲しいところが、 TABLE_NAME LIKE 'MAIN_DATA' ESCAPE '\\' なんて生成して、インデックスを使わせない SQL にしたり・・・|ι´Д`|っ

他の例もあげますと、

COLUMN_NAME LIKE '%' ESCAPE '\' なんて無くても良い SQL をわざわざ生成してインデックスを使わせない SQL にしたり・・・|ι´Д`|っ

まぁ要するに、インデックスを使わない SQL を生成する作りになっていたので激遅だったわけです。もっとも普段は column_info メソッドなんて使わないから効率を考えて無かったんだろうなぁ〜と思ったり。改造版はかなり高速になります。

それともう一つ猛烈に気になるのが

SELECT /*+ RULE*/

ってところ。近頃の Oracle はコストベースがデフォルトになってるにも関わらずルールベースで SQL を解析しろと Hint 句を使ってます。うーんうーん・・・微妙だなぁ・・・この Hint 句とっちゃう??

一応、改造した Oracle.pm を公開しておきます。何かのお役に立てればと。。。

ダウンロードはこちら → DBD::Oracle-1.17a.pm

- スポンサーリンク -