Zend_Db_Selectのcolumnsメソッドではまった
Zend_Db_Selectのメソッドにcolumnsというものがあります。
selectする対象を後付で追加できるメソッドです。使い方はこんな感じ。
1 2 3 4 5 6 7 | ※ $adapterは、Zend_Db_Adapter_Pdo_Mysql。 $adapter->select() ->from(array("t1" => "hoge_table"), array("id", "account")) ->columns(array("password", "update_date")) ->where("account = ?", "testest"); ->query()->fetchAll(); |
今回やっていたのは、2つのSQL文をUNIONで結合するという内容でした。
取得するカラムが、”id”, “account”, “state”, “update_date”として、UNIONで結合するのですが、結合する2つのSQL文の対象となるテーブルに、”state”っていうカラムがないために、後付で”0″をつけるっていうことをやっていました。(実際は複数のテーブルを結合していて、複雑なので、簡略化してます。)
1 2 3 4 5 6 7 8 9 10 11 | $select1 = $adapter->select() ->from(array("t1" => "hoge_table")) ->from(array("t2" => "hogehogehoge_table"), array("name")) ->where("t1.id = t2.account_id") $select2 = $adapter->select() ->from(array("t1" => "hogehoge_table")) ->from(array("t2" => "hogehogehoge_table"), array("name")) ->columns(array("state" => new Zend_Db_Expr(0))); $adapter->select()->union(array($select1, $select2))->query()->fetchAll(); |
テーブルのイメージはこう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | create table hoge_table ( id int , account varchar(16), state varchar(8), update_date timestamp, primary key `id` ); create table hogehoge_table ( id int, account varchar(4), update_date timestamp, primary key `id` ); create table hogehogehoge_table ( id int auto_increment, account_id int, name varchar(128), primary key `id` ); |
こういう場合、columnsで追加したカラムは、取得結果でも、後ろに追加した形ででてきます。
1 2 3 4 5 | -------------------------------------------- | id | account | update_date | name | state | -------------------------------------------- | 1 | test | 2010-11-12 | hoge | valid | --------------------------------------------- |
しかし、もともとテーブルに含まれるものについては、Zend_Db_Selectを利用しているためか、SQL文が作成される際に、並び替えられていて、stateがどの位置にくるか分かりません。(ここが問題の原因となります。)
しかも、カラム名、カラム数が一致しているのでUNIONにもエラーがでません。
columnsを利用しなかったSQL文の取得結果が、
1 2 3 4 5 | -------------------------------------------- | id | account | update_date | state | name | -------------------------------------------- | 3 | test | 2010-11-15 | valid | aaaa | --------------------------------------------- |
という順番で取得された場合、UNIONの結果は、
1 2 3 4 5 6 7 | -------------------------------------------- | id | account | update_date | state | name | -------------------------------------------- | 3 | test | 2010-11-12 | valid | aaaa | --------------------------------------------- | 1 | test | 2010-11-15 | hoge | valid | --------------------------------------------- |
となって取得されます。
項目名と違う値が入ってくるのです。今回は、互い違いになったのが数値と日付で、それをsumして合計値を取得してたために、全く予測できない値が取得されてしまいました。
解決策は、片方でcolumnsを使って、カラム名を追加した場合は、UNIONで結合するSQLは全て、同じカラム名は、columnsを使って追加する方法をとるということです。
今回の例だと。
1 2 3 4 5 6 7 8 9 10 11 12 13 | $select1 = $adapter->select() ->from(array("t1" => "hoge_table"), array("id", "account", "update_date")) ->from(array("t2" => "hogehogehoge_table"), array("name")) ->columns(array("state" => "t1.state")) ->where("t1.id = t2.account_id") $select2 = $adapter->select() ->from(array("t1" => "hogehoge_table")) ->from(array("t2" => "hogehogehoge_table"), array("name")) ->columns(array("state" => new Zend_Db_Expr(0))); $adapter->select()->union(array($select1, $select2))->query()->fetchAll(); |
と書くことです。または、columnsを使わない形で、このような書き方もできると思います。
1 2 3 4 5 6 7 8 9 10 11 12 | $select1 = $adapter->select() ->from(array("t1" => "hoge_table")) ->from(array("t2" => "hogehogehoge_table"), array("name")) ->where("t1.id = t2.account_id") $select2 = $adapter->select() ->from(array("t1" => "hogehoge_table"), array("id", "account", "state" => new Zend_Db_Expr(0), "update_date"))) ->from(array("t2" => "hogehogehoge_table"), array("name")) ->columns(array("state" => new Zend_Db_Expr(0))); $adapter->select()->union(array($select1, $select2))->query()->fetchAll(); |
テーブルのカラム数が多いとcolumnsで後付した方が、記述量が少なくなるので、どちらを使うかはその時のテーブルによるかなと思います。重要なのはcolumnsの利用の有無を揃えること。
この説明、うまくやるの難しいですね。。。