Search code examples
phpmysqlsymfonydoctrinegreatest-n-per-group

Doctrine Query Language get Max/Latest Row Per Group


I am trying and failing to translate my relatively simple SQL statement into one that will work within Doctrine.

This is the SQL statement, which works as required when run against my database:

SELECT a.*
 FROM score a
 INNER JOIN (
  SELECT name, MAX(score) AS highest
  FROM score
  GROUP BY name
 ) b
 ON a.score = b.highest AND a.name = b.name
 GROUP BY name
 ORDER BY b.highest DESC, a.dateCreated DESC

Here's the DQL attempt thus far:

$kb = $em->createQuery(
    "SELECT a 
    FROM ShmupBundle:Score a
    INNER JOIN a.name ShmupBundle:Score b WITH a.score = b.score AND a.name = b.name GROUP BY b.name
    WHERE a.platform='keyboard'
    GROUP BY a.name
    ORDER BY b.score DESC, a.dateCreated DESC"
);

Which is currently giving this error:

[Semantical Error] line 0, col 73 near 'ShmupBundle:Score': Error: Class ShmupBundle\Entity\Score has no association named name

The table itself is pretty simple: id, name, score, platform, dateCreated

There are multiple entries with the same name, but different scores. I want to show only the "high score" per name. I've been trying on and off for a day or two now, with no luck. Can anyone point me in the right direction?


Solution

  • The query you are trying to do with doctrine is related to . To use a sub query and then join with main query get things complicated to handle with doctrine. So below is the rewritten SQL version to get the same results without use of any aggregate functions:

    SELECT 
      a.* 
    FROM
      score a 
      LEFT JOIN score b 
        ON a.name = b.name 
        AND a.score < b.score 
    WHERE b.score IS NULL 
    ORDER BY a.score DESC 
    

    DEMO

    To convert above query equivalent to doctrine or DQL is easy, below is the DQL version of above SQL:

    SELECT a 
    FROM AppBundle\Entity\Score a
        LEFT JOIN AppBundle\Entity\Score b 
        WITH a.name = b.name 
        AND a.score < b.score
    WHERE b.score IS NULL
    ORDER BY a.score DESC
    

    Or with query builder you can write something like i have tested below with symfony 2.8 using the DEMO Schema

    $DM   = $this->get( 'Doctrine' )->getManager();
    $repo = $DM->getRepository( 'AppBundle\Entity\Score' );
    $results = $repo->createQueryBuilder( 'a' )
                    ->select( 'a' )
                    ->leftJoin(
                        'AppBundle\Entity\Score',
                        'b',
                        'WITH',
                        'a.name = b.name AND a.score < b.score'
                    )
                    ->where( 'b.score IS NULL' )
                    ->orderBy( 'a.score','DESC' )
                    ->getQuery()
                    ->getResult();
    

    Another idea would be create a view using your query in database and in symfony create an entity put the view name in table annotation and just start calling your entity it will give the results returned by your query but this approach is not recommended just a temporary fix.