Zend\\Db: Select from subquery

2019-09-06 09:11发布

问题:

I'm porting an application from ZF1 to ZF2 and as part of that I have to rewrite our database mappers.

I'm struggling with this SQL statement:

SELECT full_name, GROUP_CONCAT(value)
FROM (
   SELECT full_name, value
   FROM my_table
   ORDER BY id DESC
   ) as subtable
GROUP BY full_name
ORDER BY full_name DESC;

The underlying problem I'm trying to solve is that I need to order the results of the sub query before running GROUP_CONCAT and I need it to work for both MySQL and Sqlite. In MySQL I could simply specify the order within the GROUP_CONCAT function, but this is not possible with Sqlite so I need the sub query for it to be compatible with both MySQL and Sqlite.

In ZF1 I could do:

$fromSql = $db->select()
              ->from('my_table', array('full_name', 'value'))
              ->order('id DESC');

$sql = $db->select()
          ->from(array(
                'subtable' => new Zend_Db_Expr('(' . $fromSql . ')')
            ), array(
                'full_name' => 'full_name',
                'value' => new Zend_Db_Expr('GROUP_CONCAT(subtable.value)'),
            )
          )
          ->group('full_name')
          ->order('full_name DESC');

However, using a sub query in the from clause doesn't seem possible with ZF2. Is there some work around for this?

回答1:

EDIT: Actually, I now see that my query was flawed. It won't work as expected with MySQL, which means I still have to write specialized queries. See GROUP_CONCAT change GROUP BY order


After going through the code of Zend\Db\Sql\Select I found these lines:

if ($table instanceof Select) {
    $table = '(' . $this->processSubselect($table, $platform, $driver, $parameterContainer) . ')';
} else {
    $table = $platform->quoteIdentifier($table);
}

So the answer is actually quite simple, all I had to do was to provide a Zend\Db\Sql\Select object to from(), without wrapping it in a Zend\Db\Sql\Expression like I used to with ZF1.

Code example:

$adapter = $this->getAdapter(); // Returns Zend\Db\Adapter\Adapter
$sql = new Zend\Db\Sql\Sql($adapter);

$from = $sql->select()
    ->from(static::$table)
    ->columns(array(
        'full_name',
        'value',
    ))
    ->order('id DESC');

$select = $sql->select()
    ->from(array(
        'subtable' => $from,
    ))
    ->columns(array(
        'full_name' => 'full_name',
        'value' => new Expression('GROUP_CONCAT(value)'),
    ))
    ->group('full_name')
    ->order('full_name DESC');

$selectString = $sql->getSqlStringForSqlObject($select);

$resultSet = $adapter->query($selectString, $adapter::QUERY_MODE_EXECUTE);

return $resultSet->toArray();