DBIx::Class での JOIN
DBIx::Class::Manul::Cookbook の "Using joins and prefetch" の前半の抄訳です。仕事上必要になってラフに読んだので参考程度に。
リレーション対象のテーブルの1つ以上のカラムを取得したり,それでソートしたりするには,
join
アトリビュートを使います。あるアーティスト名にマッチするすべての CD を得るには以下のようにします:my $rs = $schema->resultset('CD')->search( { 'artist.name' => 'Bob Marley' }, { join => [qw/artist/], # join the artist table } ); # 以下の SQL と等価です: # SELECT cd.* FROM cd # JOIN artist ON cd.artist = artist.id # WHERE artist.name = 'Bob Marley'必要ならば
order_by
アトリビュートに含めることで,リレーション対象のテーブルのお好みのカラムでソートすることもできます。my $rs = $schema->resultset('CD')->search( { 'artist.name' => 'Bob Marley' }, { join => [qw/ artist /], order_by => [qw/ artist.name /] } }; # 以下の SQL と等価です: # SELECT cd.* FROM cd # JOIN artist ON cd.artist = artist.id # WHERE artist.name = 'Bob Marley' # ORDER BY artist.name
join
アトリビュートはリレーション対象のテーブルに存在するカラムを取得したりソートしたりするときにだけ使うようにしてください。(元の)メインテーブルのカラムだけ必要な場合にテーブルを結合するとパフォーマンスが悪化します!さて,CD のリストをアーティスト名つきで表示したいことでしょう。次のようにするとうまくいきます:
while (my $cd = $rs->next) { print "CD: " . $cd->title . ", Artist: " . $cd->artist->name; }しかしながら問題があります。主クエリで CD テーブルとアーティストテーブルを探索しましたが,データ自体は CD テーブル からしか取得していないのです。取得した CD オブジェクトに対応するアーティスト名を得るために,DBIx::Class はふたたびデータベースを検索しなくてはなりません:
SELECT artist.* FROM artist WHERE artist.id = ?主クエリで返される CD オブジェクトすべてについて,それぞれ上記のような SQL が実行されるのです。5 つの CD にたいしては 5 つのクエリが実行されます。100 枚の CD に対しては 100 ものクエリが追加実行されるのです!
ありがたいことに,この問題を解決するための
prefetch
アトリビュートというものが DBIx::Class に含まれています。このアトリビュートを用いるとリレーション先のテーブルの結果をあらかじめ取得することができます:my $rs = $schema->resultset('CD')->search( { 'artist.name' => 'Bob Marley' }, { join => [qw/ artist /], order_by => [qw/ artist.name /], prefetch => [qw/ artist /] # return artist data too! } ); # 以下の SQL と等価です("cd" と "artist" 両者を SELECT していることに注目してください): # SELECT cd.*, artist.* FROM cd # JOIN artist ON cd.artist = artist.id # WHERE artist.name = 'Bob Marley' # ORDER BY artist.nameCD の(訳注: アーティスト名つき)リストを出力するコードはそのまま使えます:
while (my $cd = $rs->next) { print "CD: " . $cd->title . ", Artist: " . $cd->artist->name; }DBIx::Class はあらかじめアーティストテーブルから合致するデータを読み込みます。ですから追加実行される SQL 文はありません。改変前より,とても効率的なクエリが発行されます。
DBIx::Class 0.05999_01 以降より,
prefetch
はhas_many
リレーションと組み合わせて使えるようになりました。
prefetch
はリレーション先のテーブルのデータを確実に使う予定があるときだけ使用してください。メインテーブルの結果だけ必要なのに,リレーション先のテーブルをプリフェッチするとやっぱりパフォーマンスは悪化しますよ!