萌えキャラとは何だったのか

ギークにも絵描きにもなれない者の末路

洒落になっていない勢いで無能を晒して首が飛びそうになってます 皆さんいかがお過ごしでしょうか mix3 です。

今回は「クエリ飛びすぎなので where in して prefetch しましょう」と言われてwhere inはわかるけどprefetchはなんぞ?となったのでとりあえずサンプル書いてみました。

例えば

erd

  • ユーザがいる
  • 装備がある
  • ユーザは装備を複数持っている
  • ユーザはアバターを複数持っている
  • アバターは複数の装備セットを持っている
  • 装備セットは頭,体,足の装備を持っている

こんな感じでなんかアバターがあって着せ替えが出来る、着せ替えさせやすいようにセットが作れるようになってるみたいな。

で、このとき50人のユーザの現在装備している装備のidを持ってこいとなったとき愚直にやると多分こんな感じ

subtest simple => sub {
    my @sqls = trace_sqls {
        for my $user_id ( 1 .. 50 ) {
            my $user_row = schema->resultset('User')->find($user_id);
            $user_row->current_ua;
            $user_row->current_ua->current_uae_set;
            $user_row->current_ua->current_uae_set->head_uae;
            $user_row->current_ua->current_uae_set->body_uae;
            $user_row->current_ua->current_uae_set->leg_uae;
            $user_row->current_ua->current_uae_set->head_uae->ae;
            $user_row->current_ua->current_uae_set->body_uae->ae;
            $user_row->current_ua->current_uae_set->leg_uae->ae;
        }
    };
    pass sprintf "count: %d", scalar(@sqls);
};

450クエリ飛ぶ

where in を使ってユーザを取ってくるところを1クエリに絞ると

subtest "where in" => sub {
    my @sqls = trace_sqls {
        my @ids = 1 .. 50;
        my $user_rs = schema->resultset('User')->search( { id => { -in => \@ids } } );
        while ( my $row = $user_rs->next ) {
            $row->current_ua;
            $row->current_ua->current_uae_set;
            $row->current_ua->current_uae_set->head_uae;
            $row->current_ua->current_uae_set->body_uae;
            $row->current_ua->current_uae_set->leg_uae;
            $row->current_ua->current_uae_set->head_uae->ae;
            $row->current_ua->current_uae_set->body_uae->ae;
            $row->current_ua->current_uae_set->leg_uae->ae;
        }
    };
    pass sprintf "count: %d", scalar(@sqls);
};

401クエリ飛ぶ where in で50クエリほど減る。

ここでprefetchを使うと

subtest "where in + prefetch" => sub {
    my @sqls = trace_sqls {
        my @ids = 1 .. 50;
        my $user_rs = schema->resultset('User')->search(
            { 'me.id' => { -in => \@ids } },
            { prefetch => {
                    current_ua => {
                        current_uae_set => {
                            head_uae => 'ae',
                            body_uae => 'ae',
                            leg_uae => 'ae',
                        },
                    },
                },
            }
        );
        while ( my $row = $user_rs->next ) {
            $row->current_ua;
            $row->current_ua->current_uae_set;
            $row->current_ua->current_uae_set->head_uae;
            $row->current_ua->current_uae_set->body_uae;
            $row->current_ua->current_uae_set->leg_uae;
            $row->current_ua->current_uae_set->head_uae->ae;
            $row->current_ua->current_uae_set->body_uae->ae;
            $row->current_ua->current_uae_set->leg_uae->ae;
        }
    };
    pass sprintf "count: %d", scalar(@sqls);
};

1クエリになる

dbic-prefetch-sample

prefetchの前にjoinというそのままのものもあるけど、joinはjoinしたテーブルの情報は返ってこないので今回のようにjoinした先のデータも必要だったらprefetchを使うと良いっぽい