3

So i have this raw SQL that i want to call via the zend framework

select t.type, t.tid,t.tname,t.cid,t.cname, ls.*
from
(
        select t.type, t.id as tid, t.name as tname, c.id as cid, c.name as cname from team t
        join company c on t.parent=c.id and t.type='C' and c.sector=20 and t.status='ACTIVE'
    union
        select t.type, t.id as tid,t.name as tname, null as cid, null as cname from team t
        join sector s on t.parent=s.id and t.type='S'and s.id=20 and t.status='ACTIVE'
) t
LEFT JOIN leaguesummary ls ON ls.leagueparticipantid=t.tid AND ls.leaguetype='T'
WHERE ls.leagueid = 5
ORDER BY ls.leaguedivision asc, ls.leagueposition asc LIMIT 10;    

I have my model class which extends Zend_Db_Table i've a simple method to build the SQL and query it

class Model_DbTable_LeagueSummary extends Zend_Db_Table_Abstract {

....

public function getTeamLeagueSummayBySector($sectorid,$limit=10)
{
    $select = $this->select()
        ->setIntegrityCheck(false)
        ->from(array('team'=>'team'),array('type','id','name'))
        ->join(array('company'=>'company'),'team.parent=company.id',array())
        ->where('team.type="C"')
        ->where('team.status="ACTIVE"')
        ->where('company.sector=?',$sectorid);

    $select2 = $this->select()
        ->setIntegrityCheck(false)
        ->from(array('team'=>'team'),array('type','id','name'))
        ->join(array('sector'=>'sector'),'team.parent=sector.id',array())
        ->where('team.type="S"')
        ->where('team.status="ACTIVE"')
        ->where('sector.id=?',$sectorid);

    // manually creating the SQL string and calling Zend_Db_Table::getDefaultAdapter() directly
    $STRING = sprintf("select x.*,ls.* from ( %s union %s ) x
        LEFT JOIN leaguesummary ls ON ls.leagueparticipantid=x.id AND ls.leaguetype='T'
        WHERE ls.leagueid = 5
        ORDER BY ls.leaguedivision asc, ls.leagueposition asc LIMIT 10;",$select,$select2);
    $db = Zend_Db_Table::getDefaultAdapter();
    $stmt = $db->query($STRING);
    $stmt->setFetchMode(Zend_Db::FETCH_OBJ);
    $result = $stmt->fetchAll();
    return $result;
}

This query works, but don't like the solution and wanted to refactor the code to use the Zend_DB methods more correctly. I've gotten this far

    $sql = $this->select()
        ->setIntegrityCheck(false)
        ->from(array('X'=>'X'))
        ->union(array($select,$select2))
        ->joinLeft(array('leaguesummary'=>'leaguesummary'),'leaguesummary.leagueparticipantid=X.id')
        ->where('leaguesummary.leaguetype="T"')
        ->where("leaguesummary.leagueid = ?",5)
        ->order("leaguesummary.leaguedivision asc")
        ->order("leaguesummary.leagueposition asc")
        ->limit($limit);
    return $db->fetchAll($sql);

But i get this exception. Whats wrong with the union statements?

Message: Invalid use of table with UNION

Stack trace:
#0 /home/assure/bhaa/zend/trunk/library/Zend/Db/Select.php(357): Zend_Db_Select->_join('left join', Array, 'leaguesummary.l...', '*', NULL)
#1 /home/assure/bhaa/zend/trunk/application/models/DbTable/LeagueSummary.php(175): Zend_Db_Select->joinLeft(Array, 'leaguesummary.l...')
#2 /home/assure/bhaa/zend/trunk/application/controllers/HousesController.php(110): Model_DbTable_LeagueSummary->getTeamLeagueSummayBySector('20')
#3 /home/assure/bhaa/zend/trunk/library/Zend/Controller/Action.php(513): HousesController->sectorAction()
#4 /home/assure/bhaa/zend/trunk/library/Zend/Controller/Dispatcher/Standard.php(289): Zend_Controller_Action->dispatch('sectorAction')
#5 /home/assure/bhaa/zend/trunk/library/Zend/Controller/Front.php(954): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
#6 /home/assure/bhaa/zend/trunk/library/Zend/Application/Bootstrap/Bootstrap.php(97): Zend_Controller_Front->dispatch()
#7 /home/assure/bhaa/zend/trunk/library/Zend/Application.php(366): Zend_Application_Bootstrap_Bootstrap->run()

-- EDIT 2 --

So i added a third step and merge the two union sql statements like this

$union = $this->getDefaultAdapter()->select()->union(array($select,$select2));
$logger->info(sprintf(' (%s) ',$union));

which gives me valid SQL

2010-06-04T15:11:55+00:00 INFO (6):  (SELECT `team`.`type`, `team`.`id`, `team`.`name` FROM `team` INNER JOIN `company` ON team.parent=company.id WHERE (team.type="C") AND (team.status="ACTIVE") AND (company.sector='20') UNION SELECT `team`.`type`, `team`.`id`, `team`.`name` FROM `team` INNER JOIN `sector` ON team.parent=sector.id WHERE (team.type="S") AND (team.status="ACTIVE") AND (sector.id='20')) 

The next step was to integrate this union clause into the main query, I've played around with the from() clause like so

->from(array('X'=> '('.$union.')' ) )
->joinLeft(array('leaguesummary'=>'leaguesummary'),'leaguesummary.leagueparticipantid=X.id')
->where('leaguesummary.leaguetype="T"')
->where("leaguesummary.leagueid = ?",5)
->order("leaguesummary.leaguedivision asc")
->order("leaguesummary.leagueposition asc")
->limit($limit);
$logger->info(sprintf('%s',$sql));
return $this->getDefaultAdapter()->fetchAll($sql);

but it seems that when the '$union' variable to converted to a string it is somehow getting shortened, which means my SQL is invalid

SELECT `X`.*, `leaguesummary`.* FROM `(SELECT ``team```.```type``, ``team``` AS `X` LEFT JOIN `leaguesummary` ON leaguesummary.leagueparticipantid=X.id WHERE (leaguesummary.leaguetype="T") AND (leaguesummary.leagueid = 5) ORDER BY `leaguesummary`.`leaguedivision` asc, `leaguesummary`.`leagueposition` asc LIMIT 10

Any ideas?

2 Answers 2

3

For a query as "complicated" as yours, you may not want to do it the "Zend" way. You can use the query() function with raw SQL

$rows = $this->getAdapter()->query("
    select t.type, t.tid,t.tname,t.cid,t.cname, ls.*
    from
    (
        select t.type, t.id as tid, t.name as tname, c.id as cid, c.name as cname from team t
        join company c on t.parent=c.id and t.type='C' and c.sector=20 and t.status='ACTIVE'
    union
        select t.type, t.id as tid,t.name as tname, null as cid, null as cname from team t
        join sector s on t.parent=s.id and t.type='S'and s.id=20 and t.status='ACTIVE'
    ) t
    LEFT JOIN leaguesummary ls ON ls.leagueparticipantid=t.tid AND ls.leaguetype='T'
    WHERE ls.leagueid = 5
    ORDER BY ls.leaguedivision asc, ls.leagueposition asc LIMIT 10;  
");

This bug report shows the proper use for the union() function:

$selectA = $db->select()
    ->from(array('u' => 'user'), 'name')
    ->where('u.id >= 5');

$selectB = $db->select()
    ->from(array('u' => 'user'), 'name')
    ->where('u.id < 5');

$select = $db->select()
    ->union(array($selectA, $selectB));

Or alternatively:

$select = $db->select()
    ->union(array(
        $db->select()
            ->from(array('u' => 'user'), 'name')
            ->where('u.id >= 5'),
        $db->select()
            ->from(array('u' => 'user'), 'name')
            ->where('u.id < 5')
));
Sign up to request clarification or add additional context in comments.

2 Comments

Zend way or no way IMO. The first idea will work, but i think generating the sub union selects from the $db reference is different to those created by $this (when in the model layer).
I ran into a situation in my code where I couldn't figure out a way to execute the query the "Zend way": stackoverflow.com/questions/2596127/…
1
    $pmSort =   'user_lname DESC';

    $qry1=$this->select()
            ->setIntegrityCheck(false)
            ->from(array('team'=>'plg_team'),array('team_id','team_name','team_sprt_id','team_user_id'))
            ->joinInner(array('user'=>'plg_users'),'user.user_id=team_user_id',array('user_fname','user_lname','user_email'))
            ->where("team_id='$pmTeamID' and team_status=1 and user_status=1");

    $qry2=$this->select()
            ->setIntegrityCheck(false)
            ->from(array('t'=>'plg_team'),array('team_id'))
            ->joinInner(array('tp'=>'plg_team_players'),'t.team_id = tp.plyr_team_id',array('plyr_id'))
            ->joinInner(array('rlsp'=>'plg_relationships'),'rlsp.rlsp_rsty_id = 2',array('rlsp_id'))
            ->joinInner(array('user'=>'plg_users'),'user.user_id = rlsp.rlsp_relation_user_id',array('user_id','user_fname','user_lname','user_email'))
            ->where("user.user_status=1 and t.team_status=1 and tp.plyr_status=1 and t.team_id='$pmTeamID' and rlsp.rlsp_user_id = tp.plyr_user_id");        

    $select = $this->select()
            ->setIntegrityCheck(false)
            ->union(array($qry1, $qry2));
    $select1 = $this->select()
            ->setIntegrityCheck(false)
            ->from(array('T'=> $select ) )
            ->order($pmSort);

    echo $select1;      

1 Comment

Please explain your solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.