简体   繁体   中英

MySQL: query should return rows with count 0

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"
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
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
);

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...

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