2014-02-12 2 views
2

기본 범위에서 INNER JOIN을 사용하면 INNER JOIN이 잘못된 테이블에 적용됩니다. 나는 Task, Project, ProjectUserUser의 4 가지 모델이 있습니다. 그들의 구조, 관계 및 기본 범위는 아래에서 볼 수 있습니다.추가 조인 대신 중첩 SELECT 사용

프로젝트는 사용자가 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는 '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를 사용하여 쿼리하도록 지시하는 방법은 무엇입니까?

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

을하고 정답을 즐길 수 : 유사한 상황에서

+0

'WHERE (SELECT ) = 0' 행에 뭔가있는 것이 있으면 실제로는 존재하지 않는 (...) 상태가되어 최적화 프로그램이 더 잘 최적화됩니다. 죄송 합니다만, 나는 당신을 도울 수 없어요. –

답변

0

나는 간단하게 할 것입니다. 그러나 SQL 자체를 작성해야하지만,이를 최적화 할 기회가 많으며 추가 요소를 추가 할 수도 있습니다. 이것은 매우 편리 할 수 ​​있습니다 그럼 당신은 어떤 상황에서 추가 특성 $tasks[0]->project_name에 액세스 할 수 있습니다 select Task.*, Project.name as project_name

:

예를 들어, 당신은 같은과 작업 테이블과도 프로젝트 이름에서 모든 것을 반환 할 수 있습니다.

Yii는 정말 유연합니다. :)