2013-07-09 4 views
2

я следующие критерии для заявления FindAllКак контролировать SQL порядок соединения в Yii CDbCriteria «с»

$with=array(
    'tumor'=>array(
    'select'=>false, 
    'joinType'=>'INNER JOIN', 
    ), 
    'tumorLibraryType'=>array(
    'select'=>false, 
    'joinType'=>'INNER JOIN', 
    'condition'=>'tumorLibraryType.id = 1 OR tumorLibraryType.id = 6', 
    ), 
    'tumorPatient'=>array(
    'select'=>false, 
    'joinType'=>'INNER JOIN', 
    ) 
); 

$libPairs=LibraryPairs::model()->with($with)->findAll(); 

Они являются соответствующие модели отношений:

'tumor' => array(self::BELONGS_TO, 'Libraries', array('tumor_library'=>'id')), 
    'normal' => array(self::BELONGS_TO, 'Libraries', array('normal_library'=>'id')),   
    // making separate AR routes for tumor and normal. only tumor used currently 
    'tumorLibraryType'=>array(self::HAS_ONE,'LibraryTypes','','on'=>'tumor.library_type_id = tumorLibraryType.id'), 
    'tumorLibrariesIsolates'=>array(self::HAS_MANY,'LibrariesIsolates',array('id'=>'library_id'),'through'=>'tumor'), 
    'tumorSamplesIsolates'=>array(self::HAS_MANY,'SamplesIsolates',array('isolate_id'=>'isolate_id'),'through'=>'tumorLibrariesIsolates'), 
    'tumorSamples'=>array(self::HAS_MANY,'Samples',array('sample_id'=>'id'),'through'=>'tumorSamplesIsolates'), 
    'tumorPatient'=>array(self::HAS_ONE,'Patients',array('patient_id'=>'id'),'through'=>'tumorSamples'), 

Этот код генерирует следующий SQL:

SELECT `t`.`tumor_library` AS `t0_c0`, `t`.`normal_library` AS `t0_c1`, `t`.`created` AS `t0_c2`, `t`.`created_by` AS `t0_c3`, `t`.`last_modified` AS `t0_c4`, `t`.`last_modified_by` AS `t0_c5`, `tumor`.`library_type_id` AS `t1_c2`, `tumor`.`id` AS `t1_c0` 
FROM `library_tumor_normal_pairs` `t` 
INNER JOIN `library_types` `tumorLibraryType` ON (tumor.library_type_id = tumorLibraryType.id) 
INNER JOIN `libraries` `tumor` ON (`t`.`tumor_library`=`tumor`.`id`) 
LEFT OUTER JOIN `libraries_isolates` `tumorLibrariesIsolates` ON (`tumor`.`id`=`tumorLibrariesIsolates`.`library_id`) 
LEFT OUTER JOIN `samples_isolates` `tumorSamplesIsolates` ON (`tumorLibrariesIsolates`.`isolate_id`=`tumorSamplesIsolates`.`isolate_id`) 
LEFT OUTER JOIN `samples` `tumorSamples` ON (`tumorSamplesIsolates`.`sample_id`=`tumorSamples`.`id`) 
INNER JOIN `patients` `tumorPatient` ON (`tumorSamples`.`patient_id`=`tumorPatient`.`id`) 
WHERE (tumorLibraryType.id = 1 OR tumorLibraryType.id = 6) 

Но что SQL выдает ошибку:

"Колонка не найдена: 1054 Неизвестная колонка 'tumor.library_type_id' в разделе" on ". "

Однако, если я просто переместить линию опухоли в SQL-запрос, чтобы быть первым присоединиться заявление, и выполнить запрос вручную, то этот запрос работает.

SELECT `t`.`tumor_library` AS `t0_c0`, `t`.`normal_library` AS `t0_c1`, `t`.`created` AS `t0_c2`, `t`.`created_by` AS `t0_c3`, `t`.`last_modified` AS `t0_c4`, `t`.`last_modified_by` AS `t0_c5`, `tumor`.`library_type_id` AS `t1_c2`, `tumor`.`id` AS `t1_c0` 
FROM `library_tumor_normal_pairs` `t` 
INNER JOIN `libraries` `tumor` ON (`t`.`tumor_library`=`tumor`.`id`) 
INNER JOIN `library_types` `tumorLibraryType` ON (tumor.library_type_id = tumorLibraryType.id) 
LEFT OUTER JOIN `libraries_isolates` `tumorLibrariesIsolates` ON (`tumor`.`id`=`tumorLibrariesIsolates`.`library_id`) 
LEFT OUTER JOIN `samples_isolates` `tumorSamplesIsolates` ON (`tumorLibrariesIsolates`.`isolate_id`=`tumorSamplesIsolates`.`isolate_id`) 
LEFT OUTER JOIN `samples` `tumorSamples` ON (`tumorSamplesIsolates`.`sample_id`=`tumorSamples`.`id`) 
INNER JOIN `patients` `tumorPatient` ON (`tumorSamples`.`patient_id`=`tumorPatient`.`id`) 
WHERE (tumorLibraryType.id = 1 OR tumorLibraryType.id = 6) 

Так что мой вопрос, как могу ли я контролировать порядок соединения sql с «критериями» в Yii? Возможно ли это? Как вы можете видеть, мой массив «с» и отношения имеют «опухоль» перед другими, но порядок объединения не сохраняется.

+0

я закончил работать вокруг этой проблемы иначе, но все же хотел бы знать, как контролировать порядок соединения, используя «с», если кто знает, как это сделать. – glyph

ответ

2

я столкнулся с подобной проблемой: Yii генерирует включается в таком порядке, что делает SQL заявление недействительным. Такая ситуация возникает, например, когда вы пытаетесь написать пользовательский $CDBCriteria->join, который опирается на таблицы, указанные в отношениях, переданных $CDBCriteria->with. Это происходит потому, что join обрабатывается в CJoinQuery::__constructor, тогда как все «стандартные» соединения (от with) генерируются Yii в CJoinQuery::join, то есть после конструктора.

К сожалению, решения отсутствуют, кроме патча. Вот пример того, как я сделал свою копию Yii (код предоставляется «как есть» - пожалуйста, проверьте, применимо ли это для вашего дела).

Во-первых, нам нужно добавить поле в CDbCriteria, которое будет включать/выключать новую функцию.

CDbCriteria.php

class CDbCriteria extends CComponent 
{ 
    ... 

    /** 
    * @var string how to join with other tables. This refers to the JOIN clause in an SQL statement. 
    * For example, <code>'LEFT JOIN users ON users.id=authorID'</code>. 
    */ 
    public $join=''; 

    /** 
    * Patch begins: 
    */ 
    public $joinreorder = false; // new option 
    ... 

Во-вторых, нам нужно расширить CJoinQuery (пожалуйста, обратите внимание, что это в CActiveFinder.php):

CActiveFinder.php

class CJoinQuery 
{ 
    ... 

    /** 
    * @var array list of join element IDs (id=>true) 
    */ 
    public $elements=array(); 

    /** 
    * Patch begins: 
    */ 
    private $joinreorder = false; // the same new option 
    private $postjoins; // the variable to store our custom joins 
    ... 

Теперь мы можем изменить конструктор CJoinQuery:

CActiveFinder.PHP (продолжение)

public function __construct($joinElement,$criteria=null) 
{ 
    if($criteria!==null) 
    { 
    $this->joinreorder = $criteria->joinreorder; 
    $this->selects[]=$joinElement->getColumnSelect($criteria->select); 
    $this->joins[]=$joinElement->getTableNameWithAlias(); 
    if($this->joinreorder)     // 
    {          // 
     $this->postjoins=$criteria->join; // new lines 
    }          // 
    else         // 
    {          // 
     $this->joins[]=$criteria->join; 
    }          // 
    $this->conditions[]=$criteria->condition; 
    $this->orders[]=$criteria->order; 

Если joinreorder является true мы храним пользовательские соединения в нашем новом переменном-члене postjoins. В противном случае все должно работать по-прежнему.

А теперь фактическое исправление в CJoinQuery::createCommand:

CActiveFinder.php (продолжение)

public function createCommand($builder) 
{ 
    $sql=($this->distinct ? 'SELECT DISTINCT ':'SELECT ') . implode(', ',$this->selects); 
    $sql.=' FROM ' . implode(' ',$this->joins); 
    if($this->joinreorder)  // 
    {       // 
    $sql .= $this->postjoins; // new lines 
    }       // 
    ... 

Наконец мы добавим обычай присоединяется в SQL заявление, и они добавляются (не предваряется как в стандартная реализация) на другие объединения, созданные из отношений Yii.

Теперь мы можем использовать его так:

$criteria = new CDbCriteria; 
$criteria->joinreorder = true; 
$criteria->with = array('product', 'shop'); 
$criteria->join = 'LEFT OUTER JOIN `shop2address` `s2a` ON (`shop`.`id` = `s2a`.`shop_id`)'; 

Без joinreorder = true этого генерирует ошибку о том, что shop.id неизвестна колонка в разделе ON, bacause «магазин» таблица еще не добавлены в SQL-заявления. С joinreorder = true он работает как ожидалось.

Что касается случаев, когда используется только with, и создается некорректная последовательность объединений, следует применять более сложный патч. Он включает в себя метод CJoinQuery::join. Он должен, возможно, иметь необязательный параметр $priority, который может быть снова заселен через соответствующий элемент, добавленный в CDbCriteria. Тогда в CJoinQuery::join изменения этих линий:

$this->joins[$element->priority]=$element->getJoinCondition(); 
$this->joins[$element->priority]=$element->relation->join; 

Это позволяет для повторного упорядочения соединений в произвольном порядке, в соответствии с установленными приоритетами.

+0

Это здорово. Я не тестировал ваше решение, но вы разместили его на форуме Yii? Он может быть включен в будущий выпуск. – glyph

+0

@glyph Возможно, я опубликую решение, когда оно будет полным значением, переупорядочивающим внутри '' '' '' '' '' '' '' '' '' '' '' '' '' – Stan

0

Эта строка выглядит неправильно:

'tumorLibraryType'=>array(self::HAS_ONE,'LibraryTypes','','on'=>'tumor.library_type_id = tumorLibraryType.id'), 

Может быть, это должно быть

'tumorLibraryType'=>array(self::HAS_ONE,'LibraryTypes',array('id'=>'library_type_id'),'through'=>'tumor'), 
+0

Это мое обходное решение для ошибки «не уникального псевдонима в таблице», когда несколько отношений проходят «через» одно и то же отношение. – glyph

+0

Хорошо. Но вы можете указать псевдоним: http://www.yiiframework.com/doc/guide/1.1/en/database.arr#relational-query-options – jborch

+0

Это не решает проблему с уникальным псевдонимом таблицы, поскольку она создает дополнительное соединение с одним и тем же псевдонимом или требует избыточных объединений и «сквозных» отношений, которые замедляют работу. Это документированная проблема, и вышеупомянутое решение является наиболее эффективным. – glyph

0

ребята, я считаю, что я опоздал на вечеринку

у меня была похожая проблема

Я критериев с слияниями:

$criteria = new CDbCriteria(); 

    $criteria->with = [ 
     'codebaseName' => [ 
      'alias' => 'cn' 
     ], 
     'codebaseProducer' => [ 
      'alias' => 'cp' 
     ], 
     'registrationDocumentLast' => [ 
      'alias' =>'rdl' 
     ] 

    ]; 

Он оказался в таком порядке заявления:

ORDER BY COALESCE(cn.name_our,cn.name_supplier), id DESC LIMIT 50 

Я не указывал порядок по id DESC явно!

После игры вокруг, я обнаружил, что он пришел из соотношения registrationDocumentLast, который был определен как

 'registrationDocumentLast' => [ 
      self::HAS_ONE, RegistrationDocument::class, 'codebase_product_pharm_id', 
      'joinType' => 'LEFT JOIN', 
      'order' => 'id DESC' 
     ], 

Посмотрите на ключ заказа. Изменение его от

  'order' => 'id DESC' 

в

  'order' => 't.id DESC' 

решена проблема

 Смежные вопросы

  • Нет связанных вопросов^_^