使用Zend生成多个子查询

时间:2013-06-14 15:07:09

标签: php sql zend-framework

我正在尝试使用Zend函数调用来重现以下SQL来构建查询:

SELECT `0`.`id`, `0`.`abbrev` FROM 

  (SELECT  `abbreviations`.`id`, `abbreviations`.`abbrev` 
   FROM  `abbreviations` ,  `keywords` 
   WHERE  `keywords`.`keyword` LIKE  'aug%'
   AND  `keywords`.`abbrev_id` =  `abbreviations`.`id`) `0`

INNER JOIN 

  (SELECT  `abbreviations`.`id` 
   FROM  `abbreviations` ,  `keywords` 
   WHERE  `keywords`.`keyword` LIKE  'foo%'
   AND  `keywords`.`abbrev_id` =  `abbreviations`.`id`) `1` 

ON (`0`.`id` =  `1`.`id`) 

INNER JOIN

  (SELECT  `abbreviations`.`id` 
   FROM  `abbreviations` ,  `keywords` 
   WHERE  `keywords`.`keyword` LIKE  'augment%'
   AND  `keywords`.`abbrev_id` =  `abbreviations`.`id`) `2` 

ON (`0`.`id` = `2`.`id`)

ORDER BY `0`.`abbrev`

我知道这个SQL的工作方式正如我测试过的那样。我宁愿使用SQL“INTERSECT”,但由于MySQL不支持(就此而言,我不知道Zend是否也支持),我不得不使用子查询。

我遇到的困难是通过使用链式函数调用创建查询来实现“Zend方式”,例如$ this-> getDbTable() - > select() - > from()等

例如,我已经能够成功创建一个子查询:

public function selectAbbrevIdsByKeyword($keyword, $abbrevFields) {
    return $this->getDbTable()->select()
    ->from(array('a' => 'abbreviations'), $abbrevFields)
    ->from(array('k' => 'keywords'), 'abbrev_id')
    ->where('`k`.`keyword` LIKE ?', $keyword . '%')
    ->where('`k`.`abbrev_id` = `a`.`id`')
    ->setIntegrityCheck(false);

然而,当我尝试将子查询组合到我的整体目标SQL语句中时,它会崩溃:

$all_abbrev_cols = array('id', 'abbrev');
$first_subselect = $this->selectAbbrevIdsByKeyword('foo', $all_abbrev_cols);
$select = $this->getDbTable()->select();
$select->from(array('0' => $first_subselect), $all_abbrev_cols);
$select->join(array("1" => 
    $this->selectAbbrevIdsByKeyword($keywords[1], 
        array('id'))), "`0`.`id` = `1`.`id`");
$select->setIntegrityCheck(false);
Zend_Debug::dump($select->__toString());

通过“分崩离析”,我的意思是产生了令人困惑的SQL,特别是所有无关的反引号。

string(1006) 
"SELECT ```id``)`.`id`, ```id``)`.`abbrev`, ```id``)`.`description`, ```id``)`.`status`, ```id``)`.`rec_practice`, ```id``)`.`type`, ```id``)`.`category`, `SELECT ``a``.``id``, ``k``.``abbrev_id`` FROM ``abbreviations`` AS ``a``
 INNER JOIN ``keywords`` AS ``k`` WHERE (``k``.``keyword`` LIKE 'aug%') AND (``k``.``abbrev_id`` = ``a``.``id``)_2`.* FROM (SELECT `a`.`id`, `a`.`abbrev`, `a`.`description`, `a`.`status`, `a`.`rec_practice`, `a`.`type`, `a`.`category`, `k`.`abbrev_id` FROM `abbreviations` AS `a`
 INNER JOIN `keywords` AS `k` WHERE (`k`.`keyword` LIKE 'foo%') AND (`k`.`abbrev_id` = `a`.`id`)) AS ```id``)`
 INNER JOIN (SELECT `a`.`id`, `k`.`abbrev_id` FROM `abbreviations` AS `a`
 INNER JOIN `keywords` AS `k` WHERE (`k`.`keyword` LIKE 'aug%') AND (`k`.`abbrev_id` = `a`.`id`)) AS `SELECT ``a``.``id``, ``k``.``abbrev_id`` FROM ``abbreviations`` AS ``a``
 INNER JOIN ``keywords`` AS ``k`` WHERE (``k``.``keyword`` LIKE 'aug%') AND (``k``.``abbrev_id`` = ``a``.``id``)_2` ON `0`.`id` = `1`.`id`"

有没有办法用“Zend方式”通过链式Zend函数调用生成SQL,或者我应该放弃并说这个查询过于复杂而是将查询构建为字符串(使用Zend_Db_Expr和/或quoteInto for parameters / quoting)?

1 个答案:

答案 0 :(得分:0)

我最终做了Aaron在评论中描述的内容,只因为它对我已有的代码最简单。

我编写了一个辅助函数,它使用标准的Zend方法根据参数构建子查询:

public function selectAbbrevIdsByKeyword($keyword, $abbrevFields) {
  return $this->getDbTable()->select()
              ->from(array('a' => 'abbreviations'), $abbrevFields)
              ->from(array('k' => 'keywords'), 'abbrev_id')
              ->where('`k`.`keyword` LIKE ?', $keyword . '%')
              ->where('`k`.`abbrev_id` = `a`.`id`')
              ->setIntegrityCheck(false)
              ->__toString();
}

然后我使用子查询辅助方法将查询构建为一个大字符串,并使用Zend_Db_Adapter的query()函数将其全部提交:

$all_abbrev_cols = array('id', 'abbrev', 'description', 'status', 'rec_practice', 'type', 'category'); 
$sql = 'SELECT DISTINCT ';
foreach ($all_abbrev_cols as $col) {
  $sql .= "`0`.`$col`, ";
}
$sql = substr_replace($sql, '', -2); // remove last comma-space
$sql .= " FROM \n\n";

// first subquery is special, has all cols and is "0"
$sql .= '  (';
$sql .= $this->selectAbbrevIdsByKeyword($keywords[0], $all_abbrev_cols);
$sql .= ") `0`\n\n";

// starting on SECOND element (index 1)
for ($i = 1; $i < count($keywords); $i++) {
  $sql .= "INNER JOIN\n\n  (";
  $sql .= $this->selectAbbrevIdsByKeyword($keywords[$i], array('id'));
  $sql .= ") `$i`\n\nON (`0`.`id` = `$i`.`id`)\n\n";
}

$sql .= 'ORDER BY `0`.`status`, `0`.`abbrev`';

$entities = array();
foreach ($this->getDb()->query($sql)->fetchAll() as $row) {
  $entities[] = $this->_populate($row);
}
return $entities;

这产生了与原始问题中的SQL语句匹配的查询结果。

相关问题