简体   繁体   中英

Doctrine: fetch with 1:n association with count() and sum()

I have a reseller entity with a 1:n relation to an activity entity.

Each activity performed by the reseller has a value.

What I want to do is to fetch all resellers with the total amount of his activity value and a count of the number of activities for a defined month and for all the year.

I have tried with the query builder:

$qb = $this->getEntityManager()->createQueryBuilder();
$qb
    ->select(array(
        'u.id', 
        'u.name', 
        'u.reseller_code', 
        'u.first_name',
        'u.last_name',
        'u.email',
        'u.country',
        'COUNT(am) as numam', 
        'SUM(ay.activity_value) as sumay', 
        'SUM(am.activity_value) as sumam'
        ))
    ->from('MyBundle:User', 'u')
    ->leftJoin('u.activities', 'ay', 'WITH', 'ay.activity_year = :year')
    ->leftJoin('u.activities', 'am', 'WITH', 'am.activity_month = :month AND am.activity_year = :year')
    ->groupBy('u.id')
    ->orderBy('u.name', 'ASC')
    ->setParameter('month', $month)
    ->setParameter('year', $year)
;

The resulted query is:

SELECT p0_.id AS id0, p0_.name AS name1, p0_.reseller_code AS reseller_code2, p0_.first_name AS first_name3, 
p0_.last_name AS last_name4, p0_.email AS email5, p0_.country AS country6, COUNT(a1_.id) AS sclr7, 
SUM(a2_.activity_value) AS sclr8, SUM(a1_.activity_value) AS sclr9 
FROM reseller p0_ 
LEFT JOIN activity a2_ ON p0_.id = a2_.reseller_id AND (a2_.activity_year = ?) 
LEFT JOIN activity a1_ ON p0_.id = a1_.reseller_id AND (a1_.activity_month = ? AND a1_.activity_year = ?) 
GROUP BY p0_.id 
ORDER BY p0_.name ASC ([2012,"2",2012])

But the result is not correct, I get numbers like 90 instead of 20 activities and amounts of 900 instead of 90.

Where I am wrong? Thanks for any help!

I think this is a SQL problem, not a Doctrine one. You are using JOIN two times on the same table, with intersecting conditions. So, you get duplicate records and wrong results for your computation.

I think this might work:

$qb = $this->getEntityManager()->createQueryBuilder();
$qb
    ->select(array(
        'u.id', 
        'u.name', 
        'u.reseller_code', 
        'u.first_name',
        'u.last_name',
        'u.email',
        'u.country',
        'COUNT(am) as numam', 
        'SUM(IF(a.activity_year = :year, a.activity_value, 0)) as sumay', 
        'SUM(IF(a.activity_year = :year AND a.activity_month = :month, a.activity_value, 0)) as sumam'
        ))
    ->from('MyBundle:User', 'u')
    ->leftJoin('u.activities', 'a')
    ->groupBy('u.id')
    ->orderBy('u.name', 'ASC')
    ->setParameter('month', $month)
    ->setParameter('year', $year)
;

Basically, the idea is to JOIN just one time, and make your computation in SUM() .

Edit: By the way, since you said 1:n relation, JOIN might be better, instead of LEFT JOIN

A bit late in the day, but CASE is currently supported by Doctrine 2; so the IF can be replaced with CASE to achieve the same result if needed.

eg

...

'SUM(CASE a.activity_year WHEN :year THEN a.activity_value ELSE 0 END) as sumay',

...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM