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.