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 'on 子句'中的未知列 'tumor.library_type_id'。”

但是,如果我只是将 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 连接顺序?可能吗?如您所见,我的“与”数组和关系在其他数组之前有“肿瘤”,但连接顺序没有保留。

4

3 回答 3

2

我遇到了一个类似的问题: Yii 以这样的顺序生成连接,使 SQL 语句无效。例如,当您尝试编写$CDBCriteria->join依赖于传递的关系中指定的表的自定义时,就会出现这种情况$CDBCriteria->with。发生这种情况是因为join处理 inCJoinQuery::__constructor而所有“标准”连接(来自with)都是由 Yii in 生成的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`)';

如果没有joinreorder = true这会生成错误,说明shop.idON 子句中的列是未知的,因为“商店”表尚未添加到 SQL 语句中。有了joinreorder = true它可以按预期工作。

对于with使用 only 并生成不正确的连接顺序的情况,应该应用更复杂的补丁。它涉及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 回答
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'),
于 2013-07-09T20:24:50.420 回答
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'

解决了问题

于 2017-10-03T21:29:22.890 回答