cakePHP で GROUP BY の壮絶バッドノウハウ 4


いろいろなところで紹介されているように
普通に $condition の中にぶちこめばいけるんですが、
こんなかんじ

$this->モデル->findAll(
        $conditions = “GROUP BY title”,
        $limit = “”
);

例えばINとか、LIKEとか使ってごりごりやっていくときって
Array で $condition つっこむのでそれのやり方のお話

普通にやると

$this->モデル->findAll(
        $conditions = array(‘Table.uid’ => Array , ‘GROUP BY Table.title’),
        $limit = “”
);

こんなかんじで、Arrayの一つとして GROUP BY を渡せばいいと思うんですが、
これじゃうまくいきません。
なぜなら Array の要素はすべて AND でつなげてしまうから。

つまりこれをSQLにすると

WHERE Table.uid=1 AND GROUP BY Table.title

なんていうSQLを投げてしまうわけです。
もちろんエラーに。

で これをどう解決するかというと
こういう風にできます


$this->モデル->findAll(
        $conditions = array(‘Table.uid’ => Array , ‘Table.a<>”” GROUP BY Table.title’),
        $limit = “”
);

*2008/03/03追記
コメントをいただいたので修正してみました。

$this->モデル->findAll(
        $conditions = array(‘Table.uid’ => Array , ‘1=1 GROUP BY Table.title’),
        $limit = “”
);

わかるかな。。。
*Table.a<>”” という条件はほしいデータの抽出にまったく影響しないダミー条件です(なんでもいいんだけど)

*2008/03/03追記
自前の1100万件のレコードのあるテーブルで上の二つをExplainしてみると
table type possible_keys key key_len ref rows Extra
Table_Name ALL NULL NULL NULL NULL 11006100 Using where
Table_Name ALL NULL NULL NULL NULL 11006108
となります。

ようは Table.a<>“” というのはWHERE句として解析して
その後で出力するんですが、
WHERE 1=1 はWHERE句の処理をせずに、無視してSQLを解析するので、
早くなります。
従って処理速度は table.a<>“” を使った時より圧倒的に早くなります。

ちなみに WHERE 1 でもOKだったので文字数を減らすならこうなります。

*2008/03/03追記
$this->モデル->findAll(
        $conditions = array(‘Table.uid’ => Array , ‘1 GROUP BY Table.title’),
        $limit = “”
);

Table.a<>”” をGROUP BY に書き加えただけなんですが、
これでうまくいきます。
ようは

WHERE Table.uid=1 AND Table.a<>”” GROUP BY Table.title

というSQLの正しい構文にするためにダミーの条件を GROUP BY が入ってるArrayのとこに書き加えてあるんです。
なんという力技。。。

ダミー条件とはいえ若干だろうけど負荷も大きくなるし、
もちろん激しく無駄w

そのままSQLかけという話もありますが
こういう方法もありますということで。


4 thoughts on “cakePHP で GROUP BY の壮絶バッドノウハウ

  1. Reply ku 6月 18,2007 1:29 PM

    cake/libs/model/datasources/dbo_source.php を見て絶望的な気分になっていたところでこのエントリを発見して救われました。ありがとうございます!

  2. Reply Jakk 6月 18,2007 5:47 PM

    リファラがTwitterのアドレスになってて
    あれ?っておもったのですw

    ついでにAddしちゃいました><

  3. Reply a 3月 2,2008 6:00 PM

    Table.a””なんて書くと、パフォーマンスに影響が出そうですね。

    1.2betaのdbo_source.test.php には以下のようなテストがありました。
    たぶん同じことをしているのでしょうが、こちらならDBMSの最適化機能で
    条件無視されそうなんでこっちのほうがよさそうです。

    $conditions = array(
    ‘Thread.project_id’ => 5,
    ‘Thread.buyer_id’ => 14,
    ‘1=1 GROUP BY Thread.project_id’
    );
    $result = $this->db->conditions($conditions);

  4. Reply Jakk 3月 3,2008 12:21 PM

    コメントありがとうございます。

    1=1

    確かにこのほうが無駄な内部SQL投げないとおもうので
    よさそうですね。

    いただいた情報を元にエントリを修正しました。
    情報ありがとうございました!

Leave a Reply