在默认范围内使用INNER JOIN时,INNER JOIN将应用于错误的表。
我有4个模型:Task
,Project
,ProjectUser
和User
。它们的结构,关系和默认范围可以在下面看到。
只有当用户被分配到项目时(通过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查询它而不是将所有连接附加到同一个表?
答案 0 :(得分:0)
在类似的情况下,我只会这样做:
$tasks = Task::model()->findBySql('your_sql_here');
并将享受正确的答案。但是你必须编写SQL本身,但是这将为你提供很多优化它的机会,甚至可以为它添加额外的元素。
例如,您可以从任务表和项目名称返回所有内容,例如:select Task.*, Project.name as project_name
然后您将可以访问额外的属性$tasks[0]->project_name
在某些情况下,这可以非常方便。
Yii非常灵活。 :)