1

I have 2 tables - User and Department and query that returns a count of users by departments. If user don't have department that returns "No department". But I needs to get also departments without user as count 0. This is my query:

SELECT COALESCE(departments.name, 'No department') AS name, count( * ) AS count
FROM users
LEFT JOIN departments ON departments.id = users.department_id
WHERE users.is_deleted = 0
AND users.company_id = 1
AND (TIMESTAMPDIFF(YEAR, `date_of_birth`, CURDATE()) BETWEEN 1 AND 18 )
GROUP BY departments.name

It should like this:

________________________
  Dep name  |  count    |
________________________
Dep 1       | 2         |
________________________
Dep 2       | 3         |
________________________
Dep 3       | 0         | if users  in this department not exist
________________________
No dep      | 1         | if users not have department
________________________

Help me, please, guys!

I found the solution

SELECT   COALESCE(locations.name, 'Without location')  AS location,
            COUNT(IF(TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE()) BETWEEN 1 AND 17, 1, NULL)) 'group_1_17'

          FROM users
          LEFT JOIN locations ON locations.id = users.location_id
          WHERE users.is_deleted = 0 AND users.company_id = :company_id
          GROUP BY locations.name

          UNION

          SELECT
            locations.name AS location,
            0 'group_1_17'         
          FROM users
          RIGHT JOIN locations ON locations.id = users.location_id
          WHERE locations.company_id = :company_id AND users.id IS NULL"
5
  • 1
    COUNT departments.id instead Commented Oct 21, 2013 at 14:53
  • Erm, instead of COUNT(*) !?! Commented Oct 21, 2013 at 15:00
  • 2
    may be you should use right join instead of left join?.. Commented Oct 21, 2013 at 15:06
  • 1
    In that case, consider providing proper DDLs and/or an sqlfiddle TOGETHER WITH THE DESIRED result set. My guess is that @ohmygirl is on the right track, but it's normally better (in the sense of 'more comprehensible') to just rewrite the query with a LEFT JOIN. Commented Oct 21, 2013 at 15:06
  • COUNT departments.id - not for my case. because it should return count users without depart. Commented Oct 21, 2013 at 15:38

3 Answers 3

3
having count(*) = 0

This is the "where" for aggregates.

SELECT COALESCE(departments.name, 'No department') AS name, count( * ) AS count
FROM users
LEFT JOIN departments ON departments.id = users.department_id
WHERE users.is_deleted = 0
AND users.company_id = 1
AND (TIMESTAMPDIFF(YEAR, `date_of_birth`, CURDATE()) BETWEEN 1 AND 18 )
GROUP BY departments.name
HAVING COUNT(*) = 0
Sign up to request clarification or add additional context in comments.

2 Comments

It not help .MySQL returned an empty result set . But should not
Can you use sqlfiddle.com to build a sample data set?
0
SELECT CASE WHEN COUNT_DEPTS=0
            THEN 'No department'
            ELSE NAME
        END ,
        COUNT_DEPTS  
FROM
(
    SELECT
       D..NAME,COUNT(D.ID) COUNT_DEPTS
      FROM USERS U
      LEFT OUTER JOIN 
           DEPARTMENTS D 
      ON U.DEPARTMENT_ID = D.ID
     WHERE U.IS_DELETED = 0
       AND U.COMPANY_ID = 1
       AND (TIMESTAMPDIFF(YEAR, `date_of_birth`, CURDATE()) BETWEEN 1 AND 18 )
    GROUP BY D.NAME
);

2 Comments

No-no-no! I needs to get also departments without user as count 0! No Department if If user don't have department
Subselects scale very badly in mysql if the data set is much larger than a toy.
0

From what I understand, you need both users without a department and departments with no users. Your left join will give you the latter, but not the former. In order to get both, you need a full join. But MySQL doesn't support full joins. And even if it did, the conditions in your where clause would eliminate the "right" part of the full join.

So the only way I can think of, besides emulating a full join, is to "manually" append the non-populated departments, with a zero count, using a union:

SELECT COALESCE(departments.name, 'No department') AS name, count( * ) AS count
FROM users
LEFT JOIN departments ON departments.id = users.department_id
WHERE users.is_deleted = 0
AND users.company_id = 1
AND (TIMESTAMPDIFF(YEAR, `date_of_birth`, CURDATE()) BETWEEN 1 AND 18 )
GROUP BY departments.name
UNION
SELECT name,0 FROM departments
WHERE id NOT IN (
  SELECT department_id FROM users
  WHERE department_id IS NOT NULL
  )

Not so elegant, granted, but it does the dirty job...

Comments

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.