2

findAll ステートメントには次の基準があります

$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) 

私の質問は、Yii で "with" 基準の SQL 結合順序を制御するにはどうすればよいですか? 出来ますか?ご覧のとおり、「with」配列とリレーションには他の配列よりも前に「腫瘍」がありますが、結合順序は保持されていません。

4

3 に答える 3

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;

場合joinreordertrue、カスタム結合を新しいメンバー変数に保存します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`)';

これがないと、'shop' テーブルがまだ SQL ステートメントに追加されていないため、ON 句の列が不明であるjoinreorder = trueというエラーが生成されます。shop.id期待どおりにjoinreorder = true動作します。

onlywithが使用され、誤った順序の結合が生成される場合については、より複雑なパッチを適用する必要があります。それにはCJoinQuery::join方法が含まれます。おそらく、オプションのパラメータを持つ必要があります$priority。これは、に追加された対応するメンバーを介して再度入力できますCDbCriteria。次に、CJoinQuery::joinこれらの行を変更します。

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

これにより、指定された優先度に従って任意の方法で結合を並べ替えることができます。

于 2013-09-23T15:48:12.373 に答える