使用doctrine 2 DBAL加入子查询

时间:2016-01-13 14:09:03

标签: php mysql doctrine-orm zend-framework2 query-builder

我正在重构Zend Framework 2 应用程序以使用doctrine 2.5 DBAL而不是Zend_DB(ZF1)。我有以下Zend_Db查询:

//Create and populate the map
int mapsize = 100;
int map[mapsize*mapsize];

for(int t = 0; t < mapsize*mapsize; t++) map[t] = 0;

//make the path
int currPos[2] = {0,50};
map[currPos[0]+(currPos[1]*mapSize)] = 1;
int landTiles = 20000;

for(var l = 0; l < landTiles; l++){
    int dir[2] = {RANDOM_BETWEEN_-1_AND_1, RANDOM_BETWEEN_-1_AND_1};
    int next[2] = {currPos[0]+dir[0], currPos[1]+dir[1]};

    map[next[0]+(next[1]*mapSize)] = 1;
    currPos = next;
}

//Draw the map
for(var row = 0; row < mapSize; row++){
    for(var col = 0; col < mapSize; col++){
        cout << map[col+(row*mapSize)];
    }
    cout << endl;
}

这导致以下MySQL查询:

$subSelect = $db->select()
    ->from('user_survey_status_entries', array('userSurveyID', 'timestamp' => 'MIN(timestamp)'))
    ->where('status = ?', UserSurveyStatus::ACCESSED)
    ->group('userSurveyID');


$select = $db->select()
    // $selectColNames contains columns both from the main query and 
    // the subquery (e.g. firstAccess.timestamp AS dateFirstAccess).
    ->from(array('us' => 'user_surveys'), $selectColNames)
    ->joinLeft(array('firstAccess' => $subSelect), 'us.userSurveyID = firstAccess.userSurveyID', array())
    ->where('us.surveyID = ?', $surveyID);

我无法使用doctrine 2.5查询构建器来弄清楚如何加入子查询。在主查询中,我需要从子查询中选择列。

我已阅读here该学说不支持加入子查询。如果仍然如此,我可以使用doctrine DBAL的SQL查询构建器以另一种方式编写此查询吗?对于我来说,原生SQL可能不是一个好的解决方案,因为此查询将在代码中稍后动态扩展。

3 个答案:

答案 0 :(得分:14)

我通过将此DQL example调整为DBAL找到了解决方案。诀窍是获取子查询的原始SQL,将其包装在括号中,然后加入它。必须在主查询中设置子查询中使用的参数:

$subSelect = $connection->createQueryBuilder()
    ->select(array('userSurveyID', 'MIN(timestamp) timestamp'))
    ->from('user_survey_status_entries')
    // Instead of setting the parameter in the main query below, it could be quoted here:
    // ->where('status = ' . $connection->quote(UserSurveyStatus::ACCESSED))
    ->where('status = :status')
    ->groupBy('userSurveyID');

$select = $connection->createQueryBuilder()
    ->select($selectColNames)
    ->from('user_surveys', 'us')
    // Get raw subquery SQL and wrap in brackets.
    ->leftJoin('us', sprintf('(%s)', $subSelect->getSQL()), 'firstAccess', 'us.userSurveyID = firstAccess.userSurveyID')
    // Parameter used in subquery must be set in main query.
    ->setParameter('status', UserSurveyStatus::ACCESSED)
    ->where('us.surveyID = :surveyID')->setParameter('surveyID', $surveyID);

答案 1 :(得分:4)

回答你问题的这一部分:

  

我无法使用doctrine 2.5查询构建器

来弄清楚如何加入子查询

您可以创建2个查询构建器实例,并在第一个查询的子句中使用第二个DQL。一个例子:

->where($qb->expr()->notIn('u.id', $qb2->getDQL())

检查示例hereherefind more using Google

答案 2 :(得分:0)

所选答案过于复杂,第二个答案不正确(如果条件完全满足,则不会返回任何内容,这意味着子查询完全没有返回任何行)。

但是,可以的,您可以重构为使用IN

$qb->leftJoin('user_survey_status_entries', 
              'firstAccess',
              Join::WITH, 
              $qb->expr()->in($qb2->getDQL())
);

对于您而言,请不要忘记将us.userSurveyID = firstAccess.userSurveyID条件移至$qb2