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の利用の有無を揃えること。


この説明、うまくやるの難しいですね。。。

  • web & pc
  • Zend_Db_Selectのcolumnsメソッドではまった はコメントを受け付けていません。
コメントは閉じられています。

return top

%d人のブロガーが「いいね」をつけました。