使用嵌套的SELECT而不是其他连接

时间:2014-02-12 08:47:58

标签: php mysql sql join yii

在默认范围内使用INNER JOIN时,INNER JOIN将应用于错误的表。 我有4个模型:TaskProjectProjectUserUser。它们的结构,关系和默认范围可以在下面看到。

只有当用户被分配到项目时(通过ProjectUser链接),才允许用户看到项目。每个项目都有很多任务。任务与其父项目具有BELONGS_TO关系。如果父项目对该用户也可见,则只允许用户查看任务。

当我查询时:

$tasks = Task::model()->withoutChildren()->findAll();

它什么都不返回。

我的模特:(这篇文章大大简化了)

class Task extends CActiveRecord {
    /*
    Columns:
    id
    parent_task_id
    project_id
    */

    public function relations() {
        return array(
            'project' => array(self::BELONGS_TO, 'Project', 'project_id'),
            'subTasks' => array(self::HAS_MANY, 'Task', 'parent_task_id', 'alias' => "Task_subTasks"),
            'parentTask' => array(self::BELONGS_TO, 'Task', 'parent_task_id'),
        );
    }


    public function scopes() {
        $t = $this->tableAlias;
        return array(
            'withoutChildren'=>array(
                'with' => array("subTasks"),
                'select' => "COUNT(Task_subTasks.id) AS subTaskCount",
                'group' => "$t.id",
                'having' => "subTaskCount = 0",
            )
        );
    }

    public function defaultScope() {
        $t = $this->getTableAlias(false, false);
        return array(
            'with' => array(
                "project" => array(
                    'select' => false,
                    'alias' => "Task_def_". $t,
                    'joinType' => "INNER JOIN"
                )
            )
        ); 
    }
}

class Project extends CActiveRecord {
    /*
    Columns:
    id
    */

    public function relations()
    {
        return array(
            'projectUsers' => array(self::HAS_MANY, 'ProjectUser', 'project_id'),
            'tasks' => array(self::HAS_MANY, 'Task', 'project_id'),
            'users' => array(self::MANY_MANY, 'User', 'project_user(project_id, user_id)'),
        );
    }

    public function defaultScope() {
        $t = $this->getTableAlias(false, false);
        return array(
            'with' => array(
                'projectUsers' => array(
                    'joinType' => "INNER JOIN",
                    'alias' => "{$t}_pu",
                    'on' => "{$t}_pu.user_id=". User::current()->id
                )
            )
        );
    }

}

class ProjectUser extends CActiveRecord {
    /*
    Columns:
    id
    project_id
    user_id
    */

    public function relations()
    {
        return array(
            'project' => array(self::BELONGS_TO, 'Project', 'project_id'),
            'user' => array(self::BELONGS_TO, 'User', 'user_id'),
        );
    }
}

class User extends CActiveRecord {

}

我明白为什么它不起作用。查询yii生成的是:(再次,大大简化)

SELECT COUNT(Task_subTasks.id) AS subTaskCount, (all the other field...) FROM `task` `t`  

INNER JOIN `project` `Task_def_t` ON (`t`.`project_id`=`Task_def_t`.`id`) AND 

INNER JOIN `project_user` `Task_def_t_pu`
ON (`Task_def_t_pu`.`project_id`=`Task_def_t`.`id`) AND
(Task_def_t_pu.user_id=6)  

LEFT OUTER JOIN
`task` `Task_subTasks` ON (`Task_subTasks`.`parent_task_id`=`t`.`id`) AND

INNER JOIN `project`
`Task_def_Task_subTasks` ON
(`Task_subTasks`.`project_id`=`Task_def_Task_subTasks`.`id`) AND

INNER JOIN `project_user`
`Task_def_Task_subTasks_pu` ON
(`Task_def_Task_subTasks_pu`.`project_id`=`Task_def_Task_subTasks`.`id`)
AND (Task_def_Task_subTasks_pu.user_id=6)  

GROUP BY t.id 

HAVING (subTaskCount = 0) 

在这种情况下,第4和第5个JOIN适用于'task' 't'表。哪个不应该发生。现在,他们更加限制原始查询,这导致查询返回0行。

然而,第4和第5个JOIN应该应用于LEFT OUTER JOIN 'task' 'Task_subTasks'(我不确定'应用'是正确的术语)。无论如何,这是我期望Yii产生的:

SELECT (all the other field...) FROM `task` `t`  

INNER JOIN `project` `Task_def_t` ON (`t`.`project_id`=`Task_def_t`.`id`) AND 

INNER JOIN `project_user` `Task_def_t_pu`
ON (`Task_def_t_pu`.`project_id`=`Task_def_t`.`id`) AND
(Task_def_t_pu.user_id=6)  

WHERE (
    SELECT COUNT(Task_subTasks.id) FROM `task` `Task_subTasks`

    INNER JOIN `project`
    `Task_def_Task_subTasks` ON
    (`Task_subTasks`.`project_id`=`Task_def_Task_subTasks`.`id`) AND

    INNER JOIN `project_user`
    `Task_def_Task_subTasks_pu` ON
    (`Task_def_Task_subTasks_pu`.`project_id`=`Task_def_Task_subTasks`.`id`)
    AND (Task_def_Task_subTasks_pu.user_id=6)  

    WHERE (`Task_subTasks`.`parent_task_id`=`t`.`id`)

) = 0

GROUP BY t.id 

我编辑了这个简单的查询而没有经过测试。但是,我以同样的方式转换了原始(大量)查询,现在它可以工作了!

我的问题:如何指示Yii使用嵌套的SELECT查询它而不是将所有连接附加到同一个表?

1 个答案:

答案 0 :(得分:0)

在类似的情况下,我只会这样做:

$tasks = Task::model()->findBySql('your_sql_here');

并将享受正确的答案。但是你必须编写SQL本身,但是这将为你提供很多优化它的机会,甚至可以为它添加额外的元素。

例如,您可以从任务表和项目名称返回所有内容,例如:select Task.*, Project.name as project_name

然后您将可以访问额外的属性$tasks[0]->project_name在某些情况下,这可以非常方便。

Yii非常灵活。 :)