Joining subqueries (possible solution)

4 messages Options
Embed this post
Permalink
Hector Virgen

Joining subqueries (possible solution)

Reply Threaded More More options
Print post
Permalink
Hello,

I came across a situation where I needed to join a table with a temporary table. I'm not sure if all databases support this feature, but MySQL does. At first, I tried joining a select object with another select object, but the resulting query ended up double-quoting my temp table's select:

$select = $table->select()
                              ->setIntegrityCheck(false)
                              ->from($table, '*')
                              ->joinInner(
                                array(
                                    'temp' => $table->select()
                                                          ->from($table, array('id' => 'MAX(id)'))
                                                          ->group('file_id')
                                ),
                                'project_file.id = temp.id',
                                array()
                              );

// Resulting SQL:
SELECT
`project_file`.*
FROM `project_file`
INNER JOIN `SELECT
MAX(id) AS ``id``
FROM ``project_file``
GROUP BY
``file_id``` AS `temp` ON project_file.id = temp.id


But, I noticed that if I pass the return value of the select's__toString() method to a new instance of Zend_Db_Expr, I get valid (and desired) SQL:

$select = $table->select()
                              ->setIntegrityCheck(false)
                              ->from($table, '*')
                              ->joinInner(
                                array(
                                    'temp' => new Zend_Db_Expr('(' . $table->select()
                                                          ->from($table, array('id' => 'MAX(id)'))
                                                          ->group('file_id') . ')'
                                ),
                                'project_file.id = temp.id',
                                array()
                              );

// SQL:

SELECT
`project_file`.*
FROM `project_file`
INNER JOIN (SELECT
MAX(id) AS `id`
FROM `project_file`
GROUP BY
`file_id`) AS `temp` ON project_file.id = temp.id

To simplify this, I've added the following lines to Zend/Db/Select.php at line 372 (right after the if/else blocks):

        // Allow joining with subqueries if $tableName is a select object
        if ($tableName instanceof Zend_Db_Select) {
            require_once 'Zend/Db/Expr.php';
            $tableName = new Zend_Db_Expr('(' . $tableName->__toString() . ')');
        }


Would it be possible to get something like this added to the Zend core? Would there be any issues with databases that don't support subqueries?

Hector Virgen

Re: Joining subqueries (possible solution)

Reply Threaded More More options
Print post
Permalink
Some javascript/style in this post has been disabled (why?)
Doh! After submitting this, I realized that a subquery (select object) should be handled in the database-aware adapter, not in the database-unaware select object.

-Hector

Hector Virgen wrote:
Hello,

I came across a situation where I needed to join a table with a temporary table. I'm not sure if all databases support this feature, but MySQL does. At first, I tried joining a select object with another select object, but the resulting query ended up double-quoting my temp table's select:

$select = $table->select()
                              ->setIntegrityCheck(false)
                              ->from($table, '*')
                              ->joinInner(
                                array(
                                    'temp' => $table->select()
                                                          ->from($table, array('id' => 'MAX(id)'))
                                                          ->group('file_id')
                                ),
                                'project_file.id = temp.id',
                                array()
                              );

// Resulting SQL:
SELECT
`project_file`.*
FROM `project_file`
INNER JOIN `SELECT
MAX(id) AS ``id``
FROM ``project_file``
GROUP BY
``file_id``` AS `temp` ON project_file.id = temp.id


But, I noticed that if I pass the return value of the select's__toString() method to a new instance of Zend_Db_Expr, I get valid (and desired) SQL:

$select = $table->select()
                              ->setIntegrityCheck(false)
                              ->from($table, '*')
                              ->joinInner(
                                array(
                                    'temp' => new Zend_Db_Expr('(' . $table->select()
                                                          ->from($table, array('id' => 'MAX(id)'))
                                                          ->group('file_id') . ')'
                                ),
                                'project_file.id = temp.id',
                                array()
                              );

// SQL:

SELECT
`project_file`.*
FROM `project_file`
INNER JOIN (SELECT
MAX(id) AS `id`
FROM `project_file`
GROUP BY
`file_id`) AS `temp` ON project_file.id = temp.id

To simplify this, I've added the following lines to Zend/Db/Select.php at line 372 (right after the if/else blocks):

        // Allow joining with subqueries if $tableName is a select object
        if ($tableName instanceof Zend_Db_Select) {
            require_once 'Zend/Db/Expr.php';
            $tableName = new Zend_Db_Expr('(' . $tableName->__toString() . ')');
        }


Would it be possible to get something like this added to the Zend core? Would there be any issues with databases that don't support subqueries?

Jack Sleight

Re: Joining subqueries (possible solution)

Reply Threaded More More options
Print post
Permalink
In reply to this post by Hector Virgen
Some javascript/style in this post has been disabled (why?)
Hi,
I'm using 1.0.4 and can't get this to work, for example:

$select = $db->select()
    ->from('a')
    ->from(array('b' => new Zend_Db_Expr('(' . $db->select()->from('c')->__toString() . ')')));
echo $select;


Gives me:

SELECT `a`.*, `b`.* FROM `a` INNER JOIN `(SELECT ``c```.`* FROM ``c``)` AS `b`

What have I done wrong? I'd really appreciate any help, as I really need sub query support, thanks.

Hector Virgen wrote:
Hello,

I came across a situation where I needed to join a table with a temporary table. I'm not sure if all databases support this feature, but MySQL does. At first, I tried joining a select object with another select object, but the resulting query ended up double-quoting my temp table's select:

$select = $table->select()
                              ->setIntegrityCheck(false)
                              ->from($table, '*')
                              ->joinInner(
                                array(
                                    'temp' => $table->select()
                                                          ->from($table, array('id' => 'MAX(id)'))
                                                          ->group('file_id')
                                ),
                                'project_file.id = temp.id',
                                array()
                              );

// Resulting SQL:
SELECT
`project_file`.*
FROM `project_file`
INNER JOIN `SELECT
MAX(id) AS ``id``
FROM ``project_file``
GROUP BY
``file_id``` AS `temp` ON project_file.id = temp.id


But, I noticed that if I pass the return value of the select's__toString() method to a new instance of Zend_Db_Expr, I get valid (and desired) SQL:

$select = $table->select()
                              ->setIntegrityCheck(false)
                              ->from($table, '*')
                              ->joinInner(
                                array(
                                    'temp' => new Zend_Db_Expr('(' . $table->select()
                                                          ->from($table, array('id' => 'MAX(id)'))
                                                          ->group('file_id') . ')'
                                ),
                                'project_file.id = temp.id',
                                array()
                              );

// SQL:

SELECT
`project_file`.*
FROM `project_file`
INNER JOIN (SELECT
MAX(id) AS `id`
FROM `project_file`
GROUP BY
`file_id`) AS `temp` ON project_file.id = temp.id

To simplify this, I've added the following lines to Zend/Db/Select.php at line 372 (right after the if/else blocks):

        // Allow joining with subqueries if $tableName is a select object
        if ($tableName instanceof Zend_Db_Select) {
            require_once 'Zend/Db/Expr.php';
            $tableName = new Zend_Db_Expr('(' . $tableName->__toString() . ')');
        }


Would it be possible to get something like this added to the Zend core? Would there be any issues with databases that don't support subqueries?


--
Jack
Jack Sleight

Re: Joining subqueries (possible solution)

Reply Threaded More More options
Print post
Permalink
I figured out what was wrong, although I don't know why this worked for Hector, perhaps this is a bug introduced in 1.0.4, but now fixed in the trunk?

Anyway, I had to change line 360 of Zend/Db/Select.php from:
if (false !== strpos($tableName, '.')) {

To:
if (!is_object($tableName) && false !== strpos($tableName, '.')) {

Jack Sleight wrote:
Hi,
I'm using 1.0.4 and can't get this to work, for example:

$select = $db->select()
    ->from('a')
    ->from(array('b' => new Zend_Db_Expr('(' .
$db->select()->from('c')->__toString() . ')')));
echo $select;

Gives me:

SELECT `a`.*, `b`.* FROM `a` INNER JOIN `(SELECT ``c```.`* FROM ``c``)`
AS `b`

What have I done wrong? I'd really appreciate any help, as I really need
sub query support, thanks.