views:

509

answers:

2

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?

+1  A: 

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')
));
Sonny
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).
emeraldjava
I ran into a situation in my code where I couldn't figure out a way to execute the query the "Zend way": http://stackoverflow.com/questions/2596127/zend-database-adapter-complex-mysql-query
Sonny
A: 
        $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;      
samy