Class::DBI::Sweetで外部結合を使いたい!
Class::DBI::Sweetで外部結合を使いたい!
目次
- [Perl][CDBI][SQL] Class::DBI::Sweetで外部結合を使いたい!
- 各RDBMSのJOIN構文
- Class::DBI::Sweet::SQL::Abstractによるマジック
- Class::DBI::Relationshipを利用する
- で、作ったのが Class::DBI::Sweet::More
Class::DBI::Sweetを使うといろいろ快適なのですが、残念ながら外部結合が使えません。
ひょっとするとJOINの構文が各種RDBMSで違うから対応していないのかと思って調べてみた。
各RDBMSのJOIN構文
MySQL:http://dev.mysql.com/doc/refman/4.1/ja/join.html
table_reference, table_reference table_reference [INNER | CROSS] JOIN table_reference [join_condition] table_reference STRAIGHT_JOIN table_reference table_reference LEFT [OUTER] JOIN table_reference join_condition table_reference NATURAL [LEFT [OUTER]] JOIN table_reference { ON table_reference LEFT OUTER JOIN table_reference ON conditional_expr } table_reference RIGHT [OUTER] JOIN table_reference join_condition table_reference NATURAL [RIGHT [OUTER]] JOIN table_reference
PostgreSQL:http://www.postgresql.jp/document/pg813doc/html/queries-table-expressions.html#QUERIES-FROM
T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 ON boolean_expression T1 { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2 USING ( join column list ) T1 NATURAL { [INNER] | { LEFT | RIGHT | FULL } [OUTER] } JOIN T2
Firebird:http://firebird.skr.jp/wiki/SELECT
= | table | view | procedure [( [, …])] [alias] = JOIN ON | ( ) = [INNER] JOIN | {LEFT | RIGHT | FULL } [OUTER]} JOIN
SQLite:http://www.net-newbie.com/sqlite/lang.html#select
table-list ::= table [join-op table join-args]* table ::= table-name [AS alias] | ( select ) [AS alias] join-op ::= , | [NATURAL] [LEFT | RIGHT | FULL] [OUTER | INNER | CROSS] JOIN join-args ::= [ON expr] [USING ( id-list )]
うーん。どれもみんな普通に使えそうだ*1。
Class::DBI::SweetのJOINを、WHEREに条件を書く形じゃなく、JOIN ONの形に変更できないものか。
そうすりゃ外部結合なんかにも対応できるようになるんじゃないかなー。has_aのテーブルは現状のままでいいけど、has_manyとかmight_haveのテーブルは外部結合になってたほうがいろいろ便利そうだし。
Class::DBI::Sweet::SQL::Abstractによるマジック
前にSweetをいじったときになんかJOINを作ってる部分があったのを思い出した。
my $joins = delete $self->{cdbi_join_info}; my $tables = delete $self->{cdbi_table_aliases}; my $from = $self->{cdbi_class}->table . " ${me}"; foreach my $join ( keys %{$joins} ) { my $table = $tables->{$join}->table; $from .= ", ${table} ${join}"; my ( $l_alias, $l_key, $f_key ) = @{ $joins->{$join} }{qw/l_alias l_key f_key/}; $sql .= " AND ${l_alias}.${l_key} = ${join}.${f_key}"; }
ここを変更すればイケそう。
ただし、無条件に外部結合にすると重くなるので、それを指示できるようにする必要がある。
Sweet側は$attributeでLEFT JOINするテーブルを指定するのがいいか。Sweet::SQL::Abstract側をどうするか、かな。
Class::DBI::Relationshipを利用する
searchメソッドの$attributeは使わずに、has_many や might_have で利用している Class::DBI::Relationship の args に設定するといい感じかも。
こんな感じ:
__PACKAGE__->has_many( cds => 'MyData::CDs', {join_type => 'LEFT'} ); __PACKAGE__->might_have( notes => 'MyData::Notes', {join_type => 'LEFT'} );
これなら既存のモジュールには影響を与えないし*2、必要なモノだけ外部結合できる。はず。
で、作ったのが Class::DBI::Sweet::More
http://search.cpan.org/~asakura/Class-DBI-Sweet-More-0.01/lib/Class/DBI/Sweet/More.pm