d.hetima RSSフィード

2007-02-25

CakePHP の findAll で効率良く recursive

  • CakePHP 1.1.13.4450 + PHP 5系 + MySQL 4.1系にてテスト
  • Job モデルは com_id フィールドがあって Com モデルに belongsTo
  • Job を一覧表示するときに Com の name フィールドも表示したい
Job.idJob.nameJob.com_id
1仕事A1
2きつい仕事2
3楽な仕事1
Com.idCom.name
1会社A
2会社B

というデータから、

Job.idJob.nameCom.name
1仕事A会社A
2きつい仕事会社B
3楽な仕事会社A

こんな感じの一覧を表示したいわけです。どうするのが効率が良いか。まず、Job モデルのアソシエーションを以下のように設定。

<?php
class Job extends AppModel {
    var $name = 'Job';
    var $belongsTo = array(
        'Com'=> array(
            'className'=>'Com',
            'foreignKey'=>'com_id',
            'conditions' => '',
            'fields' => 'id,name')
    );
?>

コントローラは以下のように。

<?php
$recursive=2; //or $this->Job->recursive = 2;
$fields='id,com_id,name';
$jobs=$this->Job->findAll($where, $fields, $cond['order'], $cond['limit'], $cond['page'], $recursive);
?>

findAll() の第6引数(recursive)に 2 を渡すと Com の name を取ってきます($jobs[]['Com']['name'])。引数を省略して、あらかじめ $this->Job->recursive=2 としておいても OK です。$where と $cond はとりあえず適切な値が入っているものとしてください。

そして、この findAll() で実行された SQL は以下の通り。

SELECT `Job`.`id`, `Job`.`com_id`, `Job`.`name`
FROM `jobs` AS `Job` LEFT JOIN `coms` AS `Com` ON `Job`.`com_id` = `Com`.`id`
WHERE 1 = 1 ORDER BY `Job`.`id` ASC LIMIT 10;

SELECT `Com`.`id`, `Com`.`name` FROM `coms` AS `Com` WHERE `Com`.`id` = '1';

SELECT `Com`.`id`, `Com`.`name` FROM `coms` AS `Com` WHERE `Com`.`id` = '2';

結果は問題ないものの SQL が3回呼ばれていました。追加クエリは2回で済んでいますが、しかしこれって、100個の異なる Com がリストアップされたら 100回呼ばれるってことですよね。

そこで、取ってくるフィールドに「Com.name」を追加し、recursive はしないようにしてみたところ、

<?php
$recursive=0; //or $this->Job->recursive = 0;
$fields='id,com_id,Com.name,name';
$jobs=$this->Job->findAll($where, $fields, $cond['order'], $cond['limit'], $cond['page'], $recursive);
?>
SELECT `Job`.`id`, `Job`.`com_id`, `Com`.`name`, `Job`.`name`
FROM `jobs` AS `Job` LEFT JOIN `coms` AS `Com` ON `Job`.`com_id` = `Com`.`id`
WHERE 1 = 1 ORDER BY `Job`.`id` ASC LIMIT 10;

一発で $jobs[]['Com']['name'] を取ってこれました。

onkn101onkn101 2007/02/26 10:50 SQL発行数が多くて困っていたのですが、このtipsはものすごく参考になりました。ありがとうございます。

bakerbaker 2007/03/01 23:16 belongsToのモデルの情報はrecursive=0で普通に全部とって来ますよ。
この例では最初にfieldsをidとnameに限定してたせいで取れなくなってるだけですね。
fieldsを特に指定しなければ、親テーブルの情報は全部取得されます。
ちなみに、belongsToもはずしたいときはrecursive=-1とするといいです。

hetimahetima 2007/03/02 01:18 findAll() に渡す $fields を null にすると、主モデルの全データ+belongsTo の fields を取ってくるようですね。そういえばデータ1件表示では、read() の $fields は null で、belongsTo の fields も取ってきていました。
一覧表示では一部の field しか使わないので現状のままで行くとします。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト

コメントを書くには、なぞなぞ認証に回答する必要があります。